为何while循环执行ssh仅返回第一个VM的uname -a结果?
为什么while循环结合SSH只能获取第一台主机的结果?
我需要在多台VM上执行uname -a命令,单独执行SSH时一切正常:
$ ssh srv-13 uname -a Linux srv-13 4.19.0-6-amd64 #1 SMP Debian 4.19.67-2 (2019-08-28) x86_64 GNU/Linux $ $ ssh akd4 uname -a Linux akd4 4.19.0-22-amd64 #1 SMP Debian 4.19.260-1 (2022-09-29) x86_64 GNU/Linux $
尝试用while循环遍历主机列表时,只执行echo命令是正常的:
$ echo 'akd4 srv-13'| tr ' ' '\n' akd4 srv-13 $ $ echo 'akd4 srv-13'| tr ' ' '\n' |while read i;do echo "==>$i";done ==>akd4 ==>srv-13 $
但在循环里加入SSH执行uname -a后,只能得到第一台主机的结果:
$ echo 'akd4 srv-13'| tr ' ' '\n' |while read i;do echo "==>$i"; ssh $i uname -a;done ==>akd4 Linux akd4 4.19.0-22-amd64 #1 SMP Debian 4.19.260-1 (2022-09-29) x86_64 GNU/Linux $
换成for循环就能得到预期的所有主机结果:
$ for i in akd4 srv-13;do ssh $i uname -a ;done Linux akd4 4.19.0-22-amd64 #1 SMP Debian 4.19.260-1 (2022-09-29) x86_64 GNU/Linux Linux srv-13 4.19.0-6-amd64 #1 SMP Debian 4.19.67-2 (2019-08-28) x86_64 GNU/Linux $
问题原因
核心原因是SSH默认会占用标准输入(stdin)。while循环里的read命令是从stdin读取主机列表的每一行,当执行第一个ssh时,ssh会把stdin中剩余的内容(也就是srv-13那一行)当作自己的输入消耗掉,导致后续的read命令再也读不到下一行,循环直接终止。
解决办法
有几种方式可以修复这个问题:
- 给SSH加
-n参数:这个参数会让SSH不读取stdin,这样read就能正常获取下一行主机名:
echo 'akd4 srv-13'| tr ' ' '\n' |while read i;do echo "==>$i"; ssh -n $i uname -a;done
- 重定向SSH的输入到
/dev/null:效果和-n参数一致,切断SSH对stdin的占用:
echo 'akd4 srv-13'| tr ' ' '\n' |while read i;do echo "==>$i"; ssh $i uname -a < /dev/null;done
- 使用for循环替代:for循环是从命令行参数中读取主机列表,不依赖stdin,因此不会受SSH的影响(就是你已经测试过的有效方法)。
内容的提问来源于stack exchange,提问作者user3313834
相关产品推荐
相关产品推荐

