通过SSH在命令中与true进行ORing


15

当我尝试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

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

Answers:


29

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"

SSH远程命令

SSH服务器启动外壳程序以运行远程命令。这是一个实际的例子:

$ 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

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

Bash shell足够聪明,可以意识到,如果通过该-c选项传递给它的命令是一个简单命令,它可以通过不实际派生新进程来进行优化,即,它直接exec使用简单命令,而不需要执行额外的步骤的fork荷兰国际集团前exec秒。这是运行远程简单命令时的示例(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

我之前已经遇到过这种行为,但是除了这个AskUbuntu答案之外,我找不到更好的参考。

杀人行为

由于pkill -f asdf || true这不是一个简单的命令(它是命令列表),因此无法进行上述优化,因此在运行时ssh pi@10.20.0.10 "pkill -f asdf || true",该sshd进程会分叉并执行bash -c "pkill -f asdf || true"

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

SSH服务器随后发现它为运行远程命令而启动的Shell进程被意外杀死,因此它向SSH客户端报告错误。


1
虽然答案正确识别一个问题,因为在源pkill,因为它的ARG列表中的正则表达式匹配杀死其父壳过程中,我会提出一个术语异议:x || y不是一个复合命令,它是一个命令列表
斯特凡Chazelas

@StéphaneChazelas感谢您的反馈。我一直打算通过bash的包容条件结构的复合命令的,但我认为,它更连贯的逻辑来考虑x||y一个命令列表。现在,我已经编辑了答案,以包含指向各种POSIX定义的链接。
安东尼G-莫妮卡的正义

1
请注意,在一般情况下,它并不能优化很多,因为它是一个命令列表,它有另一个命令要运行(可能)。在zsh// ksh93FreeBSD中shfalse || pkill -f asdfpkill在shell进程中执行。bash仅当只有一个简单命令时才执行优化。true; pkill -f asdf也将是一个问题。
斯特凡Chazelas

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

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

那讲得通!bash -c 'pgrep -af asdf'(没有|| true)没有没有发现自己。为什么不?它有-f
Gauthier

2
@Gauthier实际上,我认为在这种情况下,Bash足够聪明,可以意识到该命令是一个简单的命令(而不是复合命令),因此它可以通过不实际派生新进程来进行优化。我记得以前遇到过类似的行为(我必须更新答案)。
安东尼G-莫妮卡的大法官

3

您要求pkill杀死所有与“ asdf”匹配的东西。您应该告诉它与[a] sdf匹配,这样它仍将查找名为“ asdf”的任何内容,但看不到它本身(如果将asdf与[a] sdf对齐,请注意s与]和不是)

ssh 10.0.3.70 'pgrep -af "[a]sdf" || true'

这也是grep / egrep / awk / etc常用的技巧:

ps -ef | grep "something"  # will sometimes match itself too
ps -ef | grep "[s]omething" # will not match itself

# why it works:
# the commandline contains:     ps -ef | grep [s]omething
# and grep tries to find:                      something

这个技巧很古老,几十年前,我在Unix常见问题中看到了它(仍然很不错!)

要“自动化”它并不容易,但是通常每次您需要grep获取变量字符串regexp =“ something”时,都可以尝试执行以下操作:

grep "$(echo "${regexp}" | LC_ALL='C' sed -e 's/[a-zA-Z0-9_-]/[&]/')" 
#  if regexp="something",  it does: grep "[s]omething"
#  if regexp="otherthing", it does: grep "[o]therthing"
#  if regexp="^thirdthing", it does: grep "^[t]hirdthing" #ok, kept the "^"
#BUT fails on : regexp="[abc]def", as it does: grep "[[a]bc]def" instead of grep "[abc][d]ef" ...

注意:我知道我的'fail'grep例子,可以将regexp保持原样,因为它本身已经不匹配(a,b或c与命令行的']'不匹配) 。但是,对正则表达式进行测试并不是一件容易的事。通常,该技巧有效。自动化的大多数情况下都会起作用。否则,将需要一些聪明的技巧(或手动干预)。
Olivier Dulac

另外,(abc)?(def)?必须是([a]bc)?([d]ef)?...您无法用正则表达式解析正则表达式?> :-)
wizzwizz4

@ wizzwizz4我知道。但是您的示例已经不匹配了。这是一件复杂的事情,我只是为更简单的情况提供了一个简单的解决方案
Olivier Dulac

@ wizzwizz4我已经在我的第一条评论中这么说了……
Olivier Dulac
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.