Shell 脚本 while read 与 ssh 组合使用

发布时间:2023-04-19 20:48:00
更新时间:2023-04-19 20:50:08
文章作者:ZHUANGZHUANG

现象

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
撰写评论