Shell脚本中free命令执行异常,无法写入目标文件求助
问题:Bash脚本查询多服务器资源信息时free命令异常
脚本原代码
USER=name DEST=\result.txt FILE=\servers.txt GetTotalCores="nproc --all" GetMemoryDetails=" df -h | awk 'NR==2{print \$2}'" GetMemoryDetails2=" df -h | awk 'NR==2{print \$2}'" GetDetails="free -h" for i in `cat $FILE`; do echo -n "."q D= "$(ssh name@$i.com $GetDetails)" A="$(ssh name@$i.com $GetTotalCores)" B="$(ssh name@$i.com $GetMemoryDetails)" C="$(ssh name@$i.com $GetMemoryDetails2)" echo "${i} CPU: ${A} MEMORY: ${B} ${C} RAM: ${D} >" >> ${DEST} done exit 0
报错信息
script.sh: line 27: total used free shared buff/cache available Mem: 24669784 9181092 229024 1432604 15259668 13685616 Swap: 4063228 501760 3561468: No such file or directory
问题分析与修复
1. 变量赋值的空格错误
最核心的问题是D= "$(ssh name@$i.com $GetDetails)"这一行:Bash变量赋值时,等号前后不能有空格。你这里等号后加了空格,Bash会把"$(ssh ...)"的输出当成要执行的命令,而不是赋值给D,这就导致free -h的输出被当作命令行指令执行,从而报出“找不到文件或目录”的错误。
2. 多余的转义符与无效字符
DEST=\result.txt和FILE=\servers.txt里的反斜杠是多余的,直接写DEST=result.txt即可;echo -n "."q里的q是无效字符,应该改成echo -n "."用于进度提示;- 最后echo语句里的
>是多余的,会被Bash当作重定向符号处理,导致输出异常。
3. SSH命令的引号问题
当执行带管道或空格的远程命令时,需要用双引号包裹命令变量,避免本地Shell解析管道。比如ssh name@$i.com "$GetMemoryDetails",否则管道会在本地执行,而不是远程服务器。
4. 冗余的SSH连接
原脚本对同一服务器多次发起SSH连接,既低效又容易出问题,可以合并为一次SSH调用获取所有信息,减少连接开销。
修复后的脚本
USER=name DEST=result.txt FILE=servers.txt # 定义远程执行的命令,注意awk里的$要转义,因为是在本地字符串中 GetTotalCores="nproc --all" GetDiskUsage="df -h | awk 'NR==2{print \$2}'" GetMemDetails="free -h" for i in $(cat "$FILE"); do echo -n "." # 一次SSH连接获取所有需要的信息,用分隔符区分结果 remote_output=$(ssh "$USER@$i.com" "$GetTotalCores; $GetDiskUsage; $GetMemDetails") # 按换行分割结果,分别赋值 read -r A B <<< "$(echo "$remote_output" | head -2)" D=$(echo "$remote_output" | tail -3) echo "${i} CPU: ${A} DISK: ${B} RAM: ${D}" >> "$DEST" done echo -e "\nDone, results saved to $DEST" exit 0
额外说明
- 原脚本中
GetMemoryDetails和GetMemoryDetails2完全重复,且实际是查询磁盘(df命令),所以修复时改成了GetDiskUsage更贴合实际用途; - 使用
$(cat "$FILE")代替反引号,是更现代的Bash写法,可读性更强; - 用一次SSH连接获取所有数据,大幅提升脚本执行效率,尤其是服务器数量较多时。
内容的提问来源于stack exchange,提问作者S8Z
相关产品推荐
相关产品推荐

