Linux_Shell脚本的艰辛之路(初级篇)
一,shell脚本基础
1,shell脚本的简单小案例
写一个shell脚本,实现ping一个网站,如果ping的通,打印成功信息,否则打印失败信息
创建firstshell.sh文件,写入以下内容,保存并运行sh firstshell.sh
ping -c1 wwwwww.baidu.com &>/dev/null && echo "www.baidu.com is ok" || echo "www.baidu.com is failed"
- &>/dev/null 这个表示把前面的操作的输出写到一个文件里,null即隐藏掉,不需要看到输出结果
- && ......|| ....... 这个表示的和if/else差不多,前面条件如果成立执行&&后面的内容,否则执行||后面的内容
看下面的命令,分号是分隔同一行的多个命令,回车会执行所有的命令,但是前面的命令如果执行失败是不会影响后面的执行的,即使执行失败也会执行。
cd /usr/test;ls
2,echo输出颜色文本
输出文本的颜色:30m-37m为文字颜色,40m-47m为文字背景颜色
echo -e "\e[1;32m打印的内容\e[0m"
3,脚本中使用变量
shell脚本变量?用一个固定的字符串去表示不固定的内容
例如上面的脚本
ping -c1 wwwwww.baidu.com &>/dev/null && echo "www.baidu.com is ok" || echo "www.baidu.com is failed"
如果www.baidu.com这个地址修改了,那么至少需要修改三处,那么这里可以使用变量效果是一样的
#! /usr/bin/bash
ip=www.baidu.com
ping -c1 $ip &>/dev/null && echo "$ip is ok" || echo "$ip is failed"
再改进一下也可以这样写
#!/usr/bin/bash
ip=www.baidu.com
if ping -c1 $ip &>/dev/null;then
echo "$ip is ok"
else
echo "$ip is failed"
fi
也可以这样写
$?是上面的命令执行的结果,[ $? -eq 0 ]是对结果进行判断,执行是否成功,左右都要有空格!
#!/usr/bin/bash
ip=www.baidu.com
ping -c1 $ip &>/dev/null
if [ $? -eq 0 ];then
echo "$ip is ok"
else
echo "$ip is failed"
fi
还可以手动动态输入
#!/usr/bin/bash
read -p "请输入IP值:" ip
ping -c1 $ip &>/dev/null
if [ $? -eq 0 ];then
echo "$ip is ok"
else
echo "$ip is failed"
fi
请输入IP值:www.baidu.com
www.baidu.com is ok
4,环境变量及自定义变量
自定义变量:
- 定义变量:变量名=变量值,变量名必须以字母或下划线开头,区分大小写 ip=1
- 引用变量:$变量名 或 $
- 查看变量:echo $变量名 set(所有变量:包括自定义变量和环境变量)
- 取消变量:unset 变量名
- 作用范围:仅在当前shell中有效
环境变量:
-
定义环境变量:
- 方法一:export dir=/home/test
- 方法二:export dir 将自定义变量转换成环境变量
-
引用环境变量:$变量名或$
-
查看环境变量:echo $变量名 env 例如 env | grep dir
-
取消环境变量:unset变量名
-
变量作用范围:在当前shell和子shell中有效
5,位置变量和预定义变量
https://blog.csdn.net/Y24678750382/article/details/89362282
定义或引用变量时注意事项:
双引号是弱引用,单引号是强引用,单引号里的变量不会显示成变量值,会原封不动的显示变量名。
``命令替换 等价于$() 反引号中的shell命令会被先执行
touch `date+%F`_file.txt
这两个效果是一样的
touch $(date+%F)_file.txt
这样就创建了一个名为2021-05-07_file.txt文件
6,变量的运算 整数小数
整数运算:
-
方法一:expr
- expr 1+2
- expr $num1 + $num2
-
方法二:$(())
- echo $(($num1+$num2))
- echo $((5-3*2))
-
方法三:$[]
- echo $[5+2]
-
方法四:let(最常用)
- let sum=2+3;echo $sum
- let i++;echo $i
这里带出for循环和while循环
#!/usr/bin/bash
ip=www.baidu.com
for i in {1..5}
do
ping -c1 $ip &>/dev/null
if [ $? -eq 0 ];then
echo "$ip is up..."
fi
done
#!/usr/bin/bash
ip=www.baidu.com
i=1
while [ $i -le 5 ]
do
ping -c1 $ip &>/dev/null
if [ $? -eq 0 ];then
echo "$ip is up..."
fi
let i++
done
7,变量内容的删除和替换
其实就是截取字符串之类的操作
字符串变量截取、替换和删除:https://blog.csdn.net/qq_42491125/article/details/101368505
8,shell条件测试概述
包括文件比较、数字比较、字符串比较
格式1:test 条件表达式
格式2:[ 条件表达式 ]
格式3:[[ 条件表达式 ]]
=== 文件测试 [ 操作符 文件或目录 ]
- test -d /home 检测/home目录是否存在
- [ -d /home ] 检测/home目录是否存在
#!/usr/bin/bash
back_dir=/usr/local/mysql_back
#if ! test -d $back_dir;then 如果目录不存在,则创建目录
if [ -d $back_dir ];then
mkdir -p $back_dir
fi
echo "开始备份..."
if [ $USER != "root" ];then
echo "你没有权限!"
exit
fi
echo "开始备份..."
- [ -f file ] 是否存在,而且是文件
- [ -r file ] 当前用户对该文件是否有读权限
- [ -x file ]
- [ -w file ]
- [ -L file ] 判断是否是链接
=== 数值比较 [ 整数1 操作符 整数2 ]
- [ 1 -gt 10 ] 大于
- [ 1 -lt 10 ] 小于
- [ 1 -eq 10 ] 等于
- [ 1 -ne 10 ] 不等于
- [ 1 -ge 10 ] 大于等于
- [ 1 -le 10 ] 小于等于
这里写一个小例子,写一个脚本创建用户,如果用户存在,提示,如果不存在,创建用户
id 用户名:这个命令可以查看用户信息
#!/usr/bin/bash
read -p "Please input a username: " user
id $user &>/dev/null
if [ $? -eq 0 ];then
echo "user $user already exists"
else
useradd $user
if [ $? -eq 0 ];then
echo "$user is created successful!"
fi
fi
=== 字符串比较
提示:使用双引号
[ "$USER" = "root" ];echo $?
[ "$USER" == "root" ];echo $?
[ -z "$USER" ] 字符长度是否为0
[ -n "$USER" ] 字符长度是否不为0
变量为空或未定义,长度都为0
9,内存磁盘使用告警
再写一个小例子,如果根分区的磁盘容量如果超过90%,就报警
查看磁盘容量
[root@iZ2ze9ipfjhle3cz3cgb0rZ scripts]# df -Th
Filesystem Type Size Used Avail Use% Mounted on
devtmpfs devtmpfs 869M 0 869M 0% /dev
tmpfs tmpfs 879M 0 879M 0% /dev/shm
tmpfs tmpfs 879M 664K 878M 1% /run
tmpfs tmpfs 879M 0 879M 0% /sys/fs/cgroup
/dev/vda1 ext4 40G 25G 13G 67% /
tmpfs tmpfs 176M 0 176M 0% /run/user/996
tmpfs tmpfs 176M 0 176M 0% /run/user/0
定位到跟目录
[root@iZ2ze9ipfjhle3cz3cgb0rZ scripts]# df -Th | grep '/$'
/dev/vda1 ext4 40G 25G 13G 67% /
打印倒数第二列(也就是使用百分比)
[root@iZ2ze9ipfjhle3cz3cgb0rZ scripts]# df -Th | grep '/$' | awk '{print $(NF-1)}'
67%[root@iZ2ze9ipfjhle3cz3cgb0rZ scripts]# df -Th | grep '/$' | awk '{print $(NF-1)}' | awk -F"%" '{print $1}'
67
#!/usr/bin/bash
disk_use=`df -Th | grep '/$' | awk '{print $(NF-1)}' | awk -F"%" '{print $1}'`
if [ $disk_use -ge 60 ];then
echo "`date +%F-%H` disk: ${disk_use}% disk war..."
fi
10,case多条件匹配
#!/usr/bin/bash
case "$1" in
test)
echo "this is test"
;;
test11)
echo "this is test11"
;;
*)
echo "others"
;;
esac
[root@iZ2ze9ipfjhle3cz3cgb0rZ scripts]# sh firstshell.sh test
this is test
[root@iZ2ze9ipfjhle3cz3cgb0rZ scripts]# sh firstshell.sh test11
this is test11
[root@iZ2ze9ipfjhle3cz3cgb0rZ scripts]# sh firstshell.sh test11sdf
others
11,case删除用户判断
首先使用if判断
#!/usr/bin/bash
################################
#delete user #
#v1.0 by ftx 2021/5/7 #
################################
read -p "Please input a username: " user
id $user &>/dev/null
if [ $? -ne 0 ];then
echo "no such user: $user"
exit
fi
read -p "Are you sure?[y/n]: " action
# -o 是或者的意思
if [ "$action"="y" -o "$action"="Y" ];then
userdel -r $user
echo "$user is deleted!"
fi
其次再用case判断
#!/usr/bin/bash
################################
#delete user #
#v1.0 by ftx 2021/5/7 #
################################
read -p "Please input a username: " user
id $user &>/dev/null
if [ $? -ne 0 ];then
echo "no such user: $user"
exit
fi
read -p "Are you sure?[y/n]: " action
case "$action" in
y|Y|yes|YES)
userdel -r $user
echo "$user is deleted!"
;;
*)
echo "error"
esac
二,shell脚本实践
1,case实现jumpServer堡垒机
什么是堡垒机?堡垒机就是为了避免直接连接到主机所造成的安全性问题,每次连接主机时,需要先登录堡垒机,然后通过堡垒机再与多个主机进行连接。
在生产环境中
业务服务器不允许直接连接,允许通过从跳板机连接
业务服务器不允许root用户直接登录
实现堡垒机的思路
- 登录一个用户,这个用户提前创建好,例如alice,这个用户一登录就触发shell脚本执行
- 脚本内容是根据选择不同的选项连接不同的主机
useradd alice
passwd alice
创建了用户之后,写一个脚本
#! /usr/bin/bash
#jumpserver
# 捕捉ctrl+c 强制退出信号,什么也不做
trap "" HUP INT OUIT TSTP
clear # 清除屏幕
while :
do
cat <<-EOF
+-------------------------------------+
| 1,first host:101.201.101.206 |
| 2,second host:101.208.108.228 |
| 3,third host:192.168.101.298 |
+-------------------------------------+
EOF
# 这里为了模拟三台机器,就使用一台即可
firsthost=101.201.101.206
secondhost=101.201.101.206
thirdhost=101.201.101.206
read -p "Please select one number: " num
case "$num" in
1)
ssh root@$firsthost
;;
2)
ssh root@$secondhost
;;
3)
ssh root@$thirdhost
;;
"")
;;
*)
echo "error"
esac
done
那么如何让用户一登录就首先自动执行脚本呢?
把上面写的那个jumpserver.sh脚本放到/home/alice/下(就是哪个用户进行登录的,哪个脚本放到他的下面,就会自动执行)
然后编辑 .bash_profile文件,把脚本路径配置里面
vi .bash_profile
一定要切换到alice用户并进入到/home/alice的目录中,进行vim
并且登录root用户要授予alice用户对jumpserver.sh脚本的权限
# 查看alice用户属于哪个用户组
groups alice
# 授权限
chown alice:alice /home/alice/jumpserver.sh
chmod 777 /home/alice/jumpserver.sh
# .bash_profile
# Get the aliases and functions
if [ -f ~/.bashrc ]; then
. ~/.bashrc
fi
# User specific environment and startup programs
PATH=$PATH:$HOME/.local/bin:$HOME/bin
export PATH
/home/alice/jumpserver.sh #配上这个
测试:断开连接,使用alice用户重新连接,脚本就会登录后立即执行
执行效果如下
+-------------------------------------+
| 1,first host:101.201.101.206 |
| 2,second host:101.208.108.228 |
| 3,third host:192.168.101.298 |
+-------------------------------------+
Please select one number: 1
root@101.201.101.206's password:
Last login: Sun May 16 13:37:00 2021
Welcome to Alibaba Cloud Elastic Compute Service !
[root@iZ2ze9ipfjhle3cz3cgb0rZ ~]# cd /
[root@iZ2ze9ipfjhle3cz3cgb0rZ /]# ls
bin boot dev disk etc home lib lib64 lost+found media ml mnt opt proc root run sbin srv sys tmp usr var
[root@iZ2ze9ipfjhle3cz3cgb0rZ /]# exit
logout
Connection to 101.201.101.206 closed.
+-------------------------------------+
| 1,first host:101.201.101.206 |
| 2,second host:101.208.108.228 |
| 3,third host:192.168.101.298 |
+-------------------------------------+
Please select one number:
2,for实现批量主机ping探测
下面是简单的一个实现,写死的一个2到20的序列
#!/usr/bin/bash
# ping check
# v1.0 by ftx 2021-5-16
>ip.txt
for i in {2..20}
do
# {}&的作用是放后台执行
{
ip=192.168.122.$i
ping -c1 -W1 $ip &>/dev/null
if [ $? -eq 0 ];then
echo "$ip" |tee -a ip.txt # 把打印的信息放在ip.txt文件中
}&
done
wait # wait的作用是等待后台执行完毕
echo "finished..."
如何有指定好的几个ip怎么办呢?可以先把维护到一个txt中,然后对txt进行循环
ip.txt
192.168.1.101
192.168.1.102
192.168.1.103
#!/usr/bin/bash
>ip.txt
for i in `cat ip.txt`
do
# -W1 等待超时时间
ping -c1 -W1 $ip &>/dev/null
if [ $? -eq 0 ];then
echo "$ip is up."
else
echo "$ip is down."
fi
done
3,for实现批量用户创建
#!/usr/bin/bash
clear
while :
do
read -p "Please enter prefix & pass & num[tianyun 123 5]: " prefix pass num
printf "user infomation:
-----------------------
user prefix: prefix
user pass: pass
user num: num
----------------------
"
read -p "Are you sure?[y/n]: " action
if [ "$action" = "y" ];then
break
fi
done
for i in `seq -w $num`
do
user=$prefix$i
id $user &>/dev/null
if [ $? -eq 0 ];then
echo "user $user already exists"
else
echo "$pass" | passwd --stdin $user &>/dev/null
if [ $? -eq 0 ];then
echo "$user is created.."
fi
fi
done
实现效果
Please enter prefix & pass & num[tianyun 123 5]: ftx 23 6
user infomation:
-----------------------
user prefix: prefix
user pass: pass
user num: num
----------------------
Are you sure?[y/n]: n
Please enter prefix & pass & num[tianyun 123 5]:
4,for实现批量远程主机ssh配置
使用场景:有多台主机,需要同时修改ssh的一些配置,如果一台一台的修改就很麻烦,可以写一个脚本批量处理
首先用一个文件ip.txt存储主机ip,用于for循环
192.168.1.101
192.168.1.102
192.168.1.103
然后写脚本
#!/usr/bin/bash
for ip in `cat ip.txt`
do
{
ping -c1 -W1 $ip &>/dev/null
if [ $? -eq 0 ];then
# sed -ri 用于修改文件内容 /^这个是找以#UseDNS开头的行,/c为替换成后面的内容 这里的效果也就是把#UseDNS no改成UseDNS no
ssh $ip "sed -ri '/^#UseDNS/cUseDNS no' /etc/ssh/sshd_config"
fi
}&
done
wait
echo "all ok..."
5,while unitl 测试远程主机连接
当可以ping通时,一直while循环,直到ping不通,然后打印。
#!/usr/bin/bash
ip=101.101.202.303
while ping -c1 -W1 $ip &>/dev/null
do
sleep 1 # 当ping通时,一直while循环,每隔1秒执行
done
echo "$ip is down.."
until是相反的,如果ping不通,一直循环,直到ping通,然后打印。
#!/usr/bin/bash
ip=101.101.202.303
until ping -c1 -W1 $ip &>/dev/null
do
sleep 1
done
echo "$ip is up.."
6,Expect实现ssh非交互登录
什么是Expect?https://www.cnblogs.com/snailon/p/12529761.html
首先需要安装Expect
yum install -y expect
#! /usr/bin/expect
spawn ssh root@localhost
expect{
# 如果收到含有yes/no的消息,就自动输入yes并回车,如果没有收到消息,就继续
"yes/no" { send "yes\r"; exp_continue }
"password" { send "AIni12345\r" }
}
interact # 保持登录进入状态
然后使用expect执行脚本
expect expect.sh
由于expect安装碰见点儿问题,下次再继续!
三,shell脚本基础的深入
1,Array数组的基本概念
普通数组
赋值方式
books=(linux shell awk openstack)
books[0]=pear
books[1]=apple
根据索引取值
echo ${books[0]}
关联数组
在使用关联数组之前一定要先声明这个关联数组!declare -A info
赋值方式
info=([name]tianyu [sex]male [age]36)
info=(`cat /etc/password`) 希望是将该文件中的每个行为作为一个元数据赋值给数组
info=(tom jack "bash shell")
根据名称取值
echo ${info[name]}
2,Array数组的赋值和遍历
#!/usr/bin/bash
# 读取/etc/hosts文件的每一行,然后把每一行放到数组里面
while read line
do
hosts[++i]=$line
done </etc/hosts
echo "第一行的内容:${hosts[1]}"
echo # 空行
# ${!hosts[@]} 获取数组的每个索引
for i in ${!hosts[@]}
do
echo "$i: ${hosts[i]}"
done
3,Array实现性别统计
#!/usr/bin/bash
# 读取/etc/hosts文件的每一行,然后把每一行放到数组里面
declare -A sex # 声明关联数组
while read line
do
type=`echo $line |awk '{print $2}'` #命令的意思:拿到$line这一行,然后得到打印的第二列
let sex[$type]++
done </etc/hosts
# ${!hosts[@]} 获取数组的每个索引
for i in ${!hosts[@]}
do
echo "$i: ${sex[i]}"
done
4,Function函数的定义和调用
完成特定功能的代码块,便于复用代码,函数必须先定义才可以使用
#! /bin/bash
test() {
value=1
for((i=1;i<=5;i++))
do
value=$[$value * $i]
done
echo "5的阶乘是:$value"
}
#调用方法不用小括号
test
5,Function函数的返回值Return Out
第一种得到返回值的方式
#! /bin/bash
test() {
read -p "enter num: " num
return $[2*$num] # 返回输入数字乘以2的结果
}
#调用方法不用小括号
test
echo "test方法的返回值是:$?"
第二种得到返回值的方式(推荐)
把方法的执行结果直接赋值给一个变量即可
#! /bin/bash
test() {
read -p "enter num: " num
return $[2*$num] # 返回输入数字乘以2的结果
}
#调用方法不用小括号
result=`test`
echo "test方法的返回值是:$result"
6,函数传参
位置参数
#! /bin/bash
if [ $# -ne 3 ];then
echo "usage: `basename $0` par1 par2 par3"
exit
fi
func3() {
echo "$(($1 * $2 * $3))"
}
result=`func3 $1 $2 $3`
echo "result is: $result"
数组变量
给函数传数组参数
num=(1 2 3 4 5)
array() {
local value=1 # local 局部变量,只在本方法中有效
for i in "$@" # $@是获取所有的参数
do
value=$[value * $i]
done
echo "$value"
}
array ${num[@]}
7,Sed流编辑器
sed 是一种在线编辑器,它一次处理一行内容。
处理时,把当前处理的行存储在临时缓冲区中,称为“模式空间”(pattern space),接着用sed命令处理缓冲区中的内容,处理完成后,把缓冲区的内容送往屏幕。接着处理下一行,这样不断重复,直到文件末尾。文件内容并没有 改变,除非你使用重定向存储输出。
sed主要用来自动编辑一个或多个文件;简化对文件的反复操作;编写转换程序等。
sed命令的格式
- sed 选项 '动作' 文件名
- cat 文件名 | sed 选项 '动作'
选项:
-n :只显示被修改的行的内容
-e :直接在命令列模式上进行 sed 的动作编辑;
-f :直接将 sed 的动作写在一个文件内, -f filename 则可以运行 filename 内的 sed 动作;
-r :sed 的动作支持的是延伸型正规表示法的语法。(默认是基础正规表示法语法)
-i :直接修改读取的文件内容,而不是输出到终端。
动作:
a :在指定行后 新增一行或多行内容
c :替换指定行的内容
d :删除指定行的内容
i :在指定行之前 插入一行或多行内容
s :替换指定内容
具体操作见此文章:https://www.cnblogs.com/lfjn/p/11455970.html
四,linux定时任务
写一个linux定时任务,定时执行shell脚本。
-
crond是Linux系统用来定期执行命令或指定程序的服务的一种服务或软件。
-
一般情况下安装完成Linux操作系统之后,默认便会启动Crond任务调用服务。
-
Crond服务会定期(默认一分钟检查一次)检查系统中是否有要执行的任务工作。如果有,便根据其预先设定的定时任务规则自动执行该定时任务工作。
-
crontab -e 进入当前用户的定时任务vim编辑模式
-
crontab -l 查看当前用户的定时任务配置
-
crontab -u 用户名 -l 查看指定用户的定时任务设置
注:crontab { -l | -e } 实际上就是在操作/var/spool/cron/当前用户这样的文件,root用户就是/var/spool/cron/root
直接编辑/etc/crontab 文件,即vi /etc/crontab,添加相应的定时任务
1,定时任务表达式规则
5 * * * *每小时第5分钟执行
*/5 * * * *每5分钟执行
0 2 * * * 每天凌晨2点执行
2,直接编辑/etc/crontab 文件
minute - 从0到59的整数
hour - 从0到23的整数
day - 从1到31的整数 (必须是指定月份的有效日期)
month - 从1到12的整数 (或如Jan或Feb简写的月份)
dayofweek - 从0到7的整数,0或7用来描述周日 (或用Sun或Mon简写来表示)
root - 表示以root用户身份来运行
command - 需要执行的命令(可用as ls /proc >> /tmp/proc或 执行自定义脚本的命令)
run-parts表示后面跟着的是一个文件夹,要执行的是该文件夹下的所有脚本
对于以上各语句,星号()表示所有可用的值。例如在指代month时表示每月执行(需要符合其他限制条件)该命令。
整数间的连字号(-)表示整数列,例如1-4意思是整数1,2,3,4
指定数值由逗号分开。如:3,4,6,8表示这四个指定整数。
符号“/”指定步进设置。“/”表示步进值。如0-59/2定义每两分钟执行一次。
3,记录定时任务日志
# 不记录日志,在每一行定时任务后面加上:
> /dev/null 2>&1
# 日志记录追加到指定文件
>> /path/mylog.log 2>&1
说明:
> /dev/null 将输出重定向到/dev/null,这是个空设备,也就是忽略其输出。
> 2>&1 是将错误输出到标准输出,如果在控制台调试,也就是屏幕上,方便调试。
有3个默认的i/o,0是标准输入,一般是键盘;1是标准输出,一般是屏幕了;2是标准错误。
4,可能遇到的问题
当crontab -e编辑定时任务时,报 linux no crontab for root - using an empty one Permission denied 错误
https://blog.csdn.net/weixin_42374607/article/details/107437764
报 bash: /usr/bin/chattr: Permission denied 错误
https://blog.csdn.net/handsomezls/article/details/116130125
http://www.voidcn.com/article/p-xecghhrc-bua.html
五,Linux脚本--实现自动根据名称查询进程号并杀死进程
今天,写脚本的时候,有这么一个需求,先查询进程号,然后再kill掉进程,不过该过程要通过脚本实现。
一般来说,直接用Linux命令,可以这样
先使用 ps -ef|grep Linux-new.py 查询出进程号,然后,在用kill -9 进程号 , kill掉进程,但是,这是手工输得,程序不能手工输入呀,故写一个脚本实现
Linux_new_id=`ps -ef | grep Linux-new.py | grep -v "grep" | awk '{print $2}'`
echo $Linux_new_id
for id in $Linux_new_id
do
kill -9 $id
echo "killed $id"
done
-------------------------------------------
个性签名:独学而无友,则孤陋而寡闻。做一个灵魂有趣的人!
如果觉得这篇文章对你有小小的帮助的话,记得在右下角点个“推荐”哦,博主在此感谢!
万水千山总是情,打赏一分行不行,所以如果你心情还比较高兴,也是可以扫码打赏博主,哈哈哈(っ•̀ω•́)っ✎⁾⁾!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
· 三行代码完成国际化适配,妙~啊~