该for
循环是在这里很好。但是请注意,这是因为文件包含计算机名称,该计算机名称不包含任何空格字符或globlob字符。for x in $(cat file); do …
通常来说,它不起作用来遍历行的行file
,因为Shell首先将命令的输出分割cat file
为空白,然后将每个单词视为glob模式,因此\[?*
将其进一步扩展。如果您对其进行操作,可以确保for x in $(cat file)
安全:
set -f
IFS='
'
for x in $(cat file); do …
相关阅读:循环浏览名称中带有空格的文件?; 如何从bash中的变量逐行读取?; 为什么while IFS= read
经常使用而不是IFS=; while read..
?请注意,使用时while read
,读取行的安全语法为while IFS= read -r line; do …
。
现在,让我们看看您的while read
尝试出了什么问题。服务器列表文件中的重定向适用于整个循环。因此,在ssh
运行时,其标准输入来自该文件。ssh客户端无法知道何时远程应用程序可能希望从其标准输入中读取信息。因此,一旦ssh客户端注意到一些输入,它将把该输入发送到远程端。然后,如果需要,那里的ssh服务器就可以将该输入提供给远程命令。在您的情况下,远程命令从不读取任何输入,因此数据最终会被丢弃,但是客户端对此一无所知。您的尝试echo
之所以可行,是因为它echo
从不读取任何输入,因此仅保留其标准输入。
有几种方法可以避免这种情况。使用该-n
选项,可以告诉ssh不要从标准输入中读取。
while read server; do
ssh -n $server "uname -a"
done < /home/kenny/list_of_servers.txt
该-n
选项实际上告诉ssh
从重定向其输入/dev/null
。您可以在shell级别上执行此操作,并且该命令适用于任何命令。
while read server; do
ssh $server "uname -a" </dev/null
done < /home/kenny/list_of_servers.txt
避免ssh输入来自文件的一种诱人方法是将重定向放在read
命令:上while read server </home/kenny/list_of_servers.txt; do …
。这将不起作用,因为每次read
执行该命令时都会导致文件再次打开(因此它将一遍又一遍地读取文件的第一行)。重定向需要在整个while循环中进行,以便在循环期间将文件打开一次。
通用解决方案是在标准描述符以外的文件描述符上向循环提供输入。外壳具有将输入和输出从一个描述符号传递到另一个描述符号的构造。在这里,我们打开文件描述符3上的文件,并read
从文件描述符3重定向命令的标准输入。ssh客户端忽略打开的非标准描述符,因此一切正常。
while read server <&3; do
ssh $server "uname -a"
done 3</home/kenny/list_of_servers.txt
在bash中,该read
命令具有从其他文件描述符读取的特定选项,因此您可以写入read -u3 server
。
相关阅读:文件描述符和shell脚本;什么时候使用附加的文件描述符?