Bash while 循环在调用 subshell 时在第一次迭代后停止
Bash while loop stops after first iteration when subshell is called
这个设计的 bash 脚本演示了这个问题。
#!/bin/bash
while read -r node ; do
echo checking $node for Agent;
PID=$(ssh $node ""ps -edf | grep [j]ava | awk '{print }'"")
echo $PID got to here.
done < ~/agents_master.list
agents_master.list 每行包含 1 个服务器:
server1
server2
server3
只输出以下内容:
checking server1 for Agent
Authorized use only
25176 got to here
服务器 2 和 3 甚至没有通过行 echo checking $node...
回显到屏幕上
如果我注释掉行 PID=$(....
那么 while 会正确地完成整个 agents_master.list 文件...
checking server1 for Agent
got to here
checking server2 for Agent
got to here
checking server3 for Agent
got to here
从我所做的谷歌搜索来看,这听起来像是与 $(...)
创建的子 shell 有关,但我不明白为什么它会导致循环在第一台服务器停止,server1
.
是的,可以重写此代码,但我很想了解 bash 的这种行为以及为什么将来会发生这种情况。
问题 - 问题之一 - ssh
正在将标准输入转发到远程服务器。碰巧,您在远程服务器上 运行ning 的命令(ps -edf
,见下文)不使用其标准输入,但 ssh 仍会转发它读取的内容,以防万一。结果,read
没有任何内容可供读取,因此循环结束。
为避免这种情况,请使用 ssh -n
(或自己将输入重定向到 /dev/null
,这就是 -n
选项的作用)。
还有一些其他问题实际上并未干扰您的脚本执行。
首先,我不知道你为什么在
中使用""
ssh $node ""ps -edf | grep [j]ava | awk '{print }'""
""
"expands" 为空字符串,所以上面的内容实际上等同于
ssh $node ps -edf | grep [j]ava | awk '{print }'
这意味着 grep
和 awk
命令正在本地主机上 运行; ps
命令的输出由 ssh
转发回本地主机。这不会改变任何东西,尽管它确实使 [j]ava
中的括号变得多余,因为 grep
不会出现在进程列表中,因为它不是 运行ning执行 ps
的主机。事实上,括号是多余的是件好事,因为如果当前工作目录中恰好有一个名为 java
的文件,它们可能不会出现在命令中。你真的应该引用那个论点。
我假设您的意图是 运行 远程计算机上的整个管道,在这种情况下您可能已经尝试过:
ssh $node "ps -edf | grep [j]ava | awk '{print }'"
发现没有用。它不会起作用,因为 awk 命令中的 </code> 将扩展为当前 shell 中的任何 <code>
; </code> <em>not</em> 受内部单引号保护。就 bash 而言, <code>
只是双引号字符串的一部分。 (而且它还会将参数的问题转移到 grep
没有被引用到远程主机,所以如果远程主机的主目录中有一个名为 java
的文件,你就会遇到问题.
所以你真正想要的是
ssh -n $node 'ps -edf | grep "[j]ava" | awk "{print $2}"'
最后,不要使用 PID
作为 shell 变量的名称。所有大写的变量名通常是保留的,并且危险地接近 BASHPID
和 PPID
,它们是特定的 bash 变量。你自己的 shell 变量应该有小写的名字,就像在任何其他编程语言中一样。
这个设计的 bash 脚本演示了这个问题。
#!/bin/bash
while read -r node ; do
echo checking $node for Agent;
PID=$(ssh $node ""ps -edf | grep [j]ava | awk '{print }'"")
echo $PID got to here.
done < ~/agents_master.list
agents_master.list 每行包含 1 个服务器:
server1
server2
server3
只输出以下内容:
checking server1 for Agent
Authorized use only
25176 got to here
服务器 2 和 3 甚至没有通过行 echo checking $node...
如果我注释掉行 PID=$(....
那么 while 会正确地完成整个 agents_master.list 文件...
checking server1 for Agent
got to here
checking server2 for Agent
got to here
checking server3 for Agent
got to here
从我所做的谷歌搜索来看,这听起来像是与 $(...)
创建的子 shell 有关,但我不明白为什么它会导致循环在第一台服务器停止,server1
.
是的,可以重写此代码,但我很想了解 bash 的这种行为以及为什么将来会发生这种情况。
问题 - 问题之一 - ssh
正在将标准输入转发到远程服务器。碰巧,您在远程服务器上 运行ning 的命令(ps -edf
,见下文)不使用其标准输入,但 ssh 仍会转发它读取的内容,以防万一。结果,read
没有任何内容可供读取,因此循环结束。
为避免这种情况,请使用 ssh -n
(或自己将输入重定向到 /dev/null
,这就是 -n
选项的作用)。
还有一些其他问题实际上并未干扰您的脚本执行。
首先,我不知道你为什么在
中使用""
ssh $node ""ps -edf | grep [j]ava | awk '{print }'""
""
"expands" 为空字符串,所以上面的内容实际上等同于
ssh $node ps -edf | grep [j]ava | awk '{print }'
这意味着 grep
和 awk
命令正在本地主机上 运行; ps
命令的输出由 ssh
转发回本地主机。这不会改变任何东西,尽管它确实使 [j]ava
中的括号变得多余,因为 grep
不会出现在进程列表中,因为它不是 运行ning执行 ps
的主机。事实上,括号是多余的是件好事,因为如果当前工作目录中恰好有一个名为 java
的文件,它们可能不会出现在命令中。你真的应该引用那个论点。
我假设您的意图是 运行 远程计算机上的整个管道,在这种情况下您可能已经尝试过:
ssh $node "ps -edf | grep [j]ava | awk '{print }'"
发现没有用。它不会起作用,因为 awk 命令中的 </code> 将扩展为当前 shell 中的任何 <code>
; </code> <em>not</em> 受内部单引号保护。就 bash 而言, <code>
只是双引号字符串的一部分。 (而且它还会将参数的问题转移到 grep
没有被引用到远程主机,所以如果远程主机的主目录中有一个名为 java
的文件,你就会遇到问题.
所以你真正想要的是
ssh -n $node 'ps -edf | grep "[j]ava" | awk "{print $2}"'
最后,不要使用 PID
作为 shell 变量的名称。所有大写的变量名通常是保留的,并且危险地接近 BASHPID
和 PPID
,它们是特定的 bash 变量。你自己的 shell 变量应该有小写的名字,就像在任何其他编程语言中一样。