当调用 subshel​​l 时,Bash while 循环在第一次迭代后停止

标签 bash shell while-loop subshell

这个人为设计的 bash 脚本演示了这个问题。

#!/bin/bash
while read -r node ; do
    echo checking $node for Agent;
       PID=$(ssh $node ""ps -edf | grep [j]ava | awk '{print $2}'"")
       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 正在将 stdin 转发到远程服务器。碰巧,您在远程服务器上运行的命令(ps -edf,见下文)不使用其标准输入,但 ssh 仍会转发其读取的内容,以防万一。因此,没有任何内容可供 read 读取,因此循环结束。

为避免这种情况,请使用 ssh -n (或自行将输入重定向到 /dev/null,这就是 -n 选项确实)。

还有一些其他问题实际上不会干扰您的脚本执行。

首先,我不知道你为什么使用 ""

ssh $node ""ps -edf | grep [j]ava | awk '{print $2}'""

""“扩展”为空字符串,因此上面的内容实际上与

相同
ssh $node ps -edf | grep [j]ava | awk '{print $2}'

这意味着 grepawk 命令正在本地主机上运行; ps 命令的输出通过 ssh 转发回本地主机。这不会改变任何东西,尽管它确实使 [j]ava 中的括号变得多余,因为 grep 不会显示在进程列表中,因为它是未在执行 ps 的主机上运行。事实上,括号是多余的是件好事,因为如果当前工作目录中碰巧有一个名为 java 的文件,它们可能不会出现在命令中。你确实应该引用这个论点。

我认为您的目的是在远程计算机上运行整个管道,在这种情况下您可能已经尝试过:

ssh $node "ps -edf | grep [j]ava | awk '{print $2}'"

发现不行。它不会起作用,因为 awk 命令中的 $2 将扩展到当前 shell 中的任何 $2$2 不受内部单引号保护。就 bash 而言,$2 只是双引号字符串的一部分。 (并且它还会将参数问题转移到 grep 未引用到远程主机,因此如果在远程主机上的主目录。

所以你真正想要的是

ssh -n $node 'ps -edf | grep "[j]ava" | awk "{print \$2}"'

最后,不要使用 PID 作为 shell 变量的名称。全大写的变量名通常是保留的,它非常接近 BASHPIDPPID,它们是特定的 bash 变量。您自己的 shell 变量应该具有小写名称,就像在任何其他编程语言中一样。

关于当调用 subshel​​l 时,Bash while 循环在第一次迭代后停止,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37200100/

相关文章:

linux - 使用正则表达式将 bash 脚本转换为 sh

linux - 将文件重命名为数字,从特定数字开始

shell - Unix 与远程隐藏文件的并排区别

php - MySQL:PHP while 与查询和另一个 while 与查询,如何改进?

python - 无法跳出 while 循环

bash - bash 中的 ":"被称为什么?冒号用作传递语句

linux - bash脚本中的十进制值变为全零

shell - 可以将命令别名自动添加到DOS shell中吗?

c - While 循环的输出

linux - awk inside shell脚本显示/etc/passwd信息