在 ssh 命令中与 true 进行 ORing

Gau*_*ier 14 shell ssh kill

当我尝试pkill -f通过 ssh 远程运行并尝试丢弃可能的错误代码(即使没有找到进程也继续执行脚本的其余部分)时,|| true行为与我预期的不一样。

$ pkill asdf || true
$ echo $?
0
$ pkill -f asdf || true
$ echo $?
0
$ ssh pi@10.20.0.10 "pkill asdf || true"
$ echo $?
0
$ ssh pi@10.20.0.10 "pkill -f asdf || true"
255
Run Code Online (Sandbox Code Playgroud)

我想是ssh返回 255,而不是引号之间的命令,但为什么呢?

Ant*_*gan 30

您认为它ssh本身返回 255 退出状态的假设是正确的。该ssh手册页指出:

如果发生错误,ssh 会以远程命令的退出状态或 255 退出。

如果您只是简单地运行ssh pi@10.20.0.10 "pkill -f asdf",您很可能会得到一个退出状态1,对应pkill于“没有进程匹配”的状态。

具有挑战性的部分是理解为什么在运行时 SSH 会发生错误

ssh pi@10.20.0.10 "pkill -f asdf || true"
Run Code Online (Sandbox Code Playgroud)

SSH 远程命令

SSH 服务器启动一个 shell 来运行远程命令。这是一个实际的例子:

$ ssh server "ps -elf | tail -5"
4 S root     35323  1024 12  80   0 - 43170 poll_s 12:01 ?        00:00:00 sshd: anthony [priv]
5 S anthony  35329 35323  0  80   0 - 43170 poll_s 12:01 ?        00:00:00 sshd: anthony@notty
0 S anthony  35330 35329  0  80   0 - 28283 do_wai 12:01 ?        00:00:00 bash -c ps -elf | tail -5
0 R anthony  35341 35330  0  80   0 - 40340 -      12:01 ?        00:00:00 ps -elf
0 S anthony  35342 35330  0  80   0 - 26985 pipe_w 12:01 ?        00:00:00 tail -5
Run Code Online (Sandbox Code Playgroud)

请注意,默认 shell 是bash并且远程命令不是一个简单的命令,而是一个管道,“由控制操作符分隔的一个或多个命令的序列|”。

Bash shell 足够聪明,它意识到如果-c选项传递给它的命令是一个简单的命令,它可以通过不实际分叉新进程来优化,即它直接exec是简单的命令而不是经过额外的步骤forkexecs之前的ing 。这是运行远程简单命令(ps -elf在本例中)时发生的情况的示例:

$ ssh server "ps -elf" | tail -5
1 S root     34740     2  0  80   0 -     0 worker 11:49 ?        00:00:00 [kworker/0:1]
1 S root     34762     2  0  80   0 -     0 worker 11:50 ?        00:00:00 [kworker/0:3]
4 S root     34824  1024 31  80   0 - 43170 poll_s 11:51 ?        00:00:00 sshd: anthony [priv]
5 S anthony  34829 34824  0  80   0 - 43170 poll_s 11:51 ?        00:00:00 sshd: anthony@notty
0 R anthony  34830 34829  0  80   0 - 40340 -      11:51 ?        00:00:00 ps -elf
Run Code Online (Sandbox Code Playgroud)

我以前遇到过这种行为,但除了这个 AskUbuntu answer之外,我找不到更好的参考。

pkill 行为

由于pkill -f asdf || true不是一个简单的命令(它是一个命令列表),上面的优化不能发生,所以当你运行时ssh pi@10.20.0.10 "pkill -f asdf || true"sshd进程 fork 和 execs bash -c "pkill -f asdf || true"

正如 ctx 的回答指出的那样,pkill不会杀死自己的进程。但是,它杀死命令行与-f模式匹配的任何其他进程。该bash -c命令匹配此模式,因此它会终止此进程 - 它自己的父进程(发生时)。

SSH 服务器然后看到它为运行远程命令而启动的 shell 进程意外终止,因此它向 SSH 客户端报告错误。


ctx*_*ctx 9

您的远程命令会自行杀死:

$ ssh 10.0.3.70 'pgrep -af asdf'
$ ssh 10.0.3.70 'pgrep -af asdf || true'
1018 bash -c pgrep -af asdf || true
Run Code Online (Sandbox Code Playgroud)

pgrep 和 pkill 将忽略它们自己的进程,但使用 -f 标志,它们将找到父 shell:

$ pgrep -af asdf
$ pgrep -af asdf || true
$ bash -c 'pgrep -af asdf'
$ bash -c 'pgrep -af asdf || true'
9803 bash -c pgrep -af asdf || true
Run Code Online (Sandbox Code Playgroud)

  • @Gauthier 实际上,我认为在这种情况下,Bash 足够聪明,可以意识到该命令是一个简单的命令(不是复合命令),因此它通过实际上不分叉新进程来进行优化。我记得以前遇到过类似的行为(我必须更新我的答案)。 (2认同)