【问题标题】:Bash while loop stops after first iteration when subshell is called [duplicate]当调用子shell时,Bash while循环在第一次迭代后停止[重复]
【发布时间】:2016-09-09 01:35:18
【问题描述】:

这个人为的 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 的这种行为以及为什么会在未来发生这种情况。

【问题讨论】:

  • PID=$(ps -edopid=,command= | awk '/[j]ava/{print $1}') 为您节省了管道和浪费的过程。假设您使用的是 Linux。

标签: bash shell while-loop subshell


【解决方案1】:

问题——其中一个问题——是ssh 正在将标准输入转发到远程服务器。碰巧的是,您在远程服务器上运行的命令(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 没有被引用到远程主机,所以如果远程主机的主目录中有一个名为java 的文件,您就会遇到问题。

所以你真正想要的是

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

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

【讨论】:

  • 非常感谢您的详细回复。
  • 太棒了。我可以删除我的:) +1
  • 比“重复”问题更好的问答。
猜你喜欢
  • 2014-10-02
  • 2015-08-07
  • 2015-10-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-27
  • 1970-01-01
相关资源
最近更新 更多