现象
while read 与 ssh 搭配使用时,只执行了一次循环体,第二次直接退出了
192.168.111.101
192.168.111.101
192.168.111.101
#!/bin/bash
# date.sh
while read ip
do
echo -n "$ip: "
ssh $ip "date"
done < ip.list
bash date.sh
192.168.111.101: Wed Apr 19 18:22:57 CST 2023
bash -x date.sh
+ read ip
+ echo -n '192.168.111.101: '
192.168.111.101: + ssh 192.168.111.101 date
Wed Apr 19 18:24:18 CST 2023
+ read ip
当注释掉 ssh 执行命令时,循环正常
#!/bin/bash
# date.sh
while read ip
do
echo -n "$ip: "
# ssh $ip "date"
done < ip.list
bash date.sh
192.168.111.101: 192.168.111.101: 192.168.111.101:
+ read ip
+ echo -n '192.168.111.101: '
192.168.111.101: + read ip
+ echo -n '192.168.111.101: '
192.168.111.101: + read ip
+ echo -n '192.168.111.101: '
192.168.111.101: + read ip
原因
执行 while 语句时,ip.list 文件中的内容都已经读入缓冲区并重定向给了整个 while 语句,read 从标准输入里读取数据并赋值给变量 ip,ssh 命令默认也从标准输入里读取数据,就会把刚才读入 while 循环里缓冲区的内容消耗掉。当第二次 read 时就无法获取标准输入,while 循环退出。
#!/bin/bash
# date.sh
while read ip
do
echo -n "$ip: "
ssh $ip "echo; cat"
done < ip.list
bash date.sh
192.168.111.101: # 第一次循环 ssh 之前
192.168.111.101 # 第一次循环 ssh cat 到标准输入的第一行,ip.list 文件中的第二行
192.168.111.101 # 第一次循环 ssh cat 到标准输入的第二行,ip.list 文件中的第三行
解决
方法一
解决 ssh 读取标准输入
#!/bin/bash
# date.sh
while read ip
do
echo -n "$ip: "
# ssh $ip "date" < /dev/null # 通过指定黑洞设备
# echo | ssh $ip "date" # 利用管道给 ssh 标准输入
ssh -n $ip "date" # 通过 ssh 选项
done < ip.list
bash date.sh
192.168.111.101: Wed Apr 19 18:42:34 CST 2023
192.168.111.101: Wed Apr 19 18:42:34 CST 2023
192.168.111.101: Wed Apr 19 18:42:35 CST 2023
方法二
解决 read 读取标准输入
#!/bin/bash
# date.sh
exec 7< ip.list # 指定文件描述符
while read -u7 ip # read 读取文件描述符
do
echo -n "$ip: "
ssh $ip "date"
done
exec 7>&- # 关闭文件描述符
bash date.sh
192.168.111.101: Wed Apr 19 18:52:41 CST 2023
192.168.111.101: Wed Apr 19 18:52:42 CST 2023
192.168.111.101: Wed Apr 19 18:52:42 CST 2023
将文件描述符中的内容消耗完成,循环退出。
#!/bin/bash
# date.sh
exec 7< ip.list
while read -u7 ip
do
cat <&7
echo -n "$ip: "
ssh $ip "date"
done
exec 7>&-
bash date.sh
192.168.111.101 # ip.list 文件第二行内容,第一行已被 read 读取到变量中
192.168.111.101 # ip.list 文件第三行内容
192.168.111.101: Wed Apr 19 18:55:01 CST 2023 # 循环体 cat 后的执行结果
循环只执行了一次,当第二次执行时,文件描述符中的内容已经被消耗,read 无法读取到新的内容给变量,循环退出。
方法三
使用 for 循环代替 while 循环
#!/bin/bash
# date.sh
for ip in $(cat ip.list)
do
echo -n "$ip: "
ssh $ip "date"
done
bash date.sh
192.168.111.101: Wed Apr 19 20:29:58 CST 2023
192.168.111.101: Wed Apr 19 20:29:58 CST 2023
192.168.111.101: Wed Apr 19 20:29:59 CST 2023