在 ssh 命令中与 true 进行或运算

在 ssh 命令中与 true 进行或运算

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

$ pkill asdf || true
$ echo $?
0
$ pkill -f asdf || true
$ echo $?
0
$ ssh [email protected] "pkill asdf || true"
$ echo $?
0
$ ssh [email protected] "pkill -f asdf || true"
255

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

答案1

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

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

如果您只是运行,您很可能会得到退出状态,对应于“的状态ssh [email protected] "pkill -f asdf"1pkill没有匹配的进程”。

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

ssh [email protected] "pkill -f asdf || true"

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

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

Bash shell 足够聪明,能够意识到如果选项传递给它的命令-c简单的命令,它可以通过不实际分叉一个新进程来进行优化,即,它直接执行简单命令,而不是在执行之前exec执行额外的 ing 步骤。以下是运行远程简单命令时发生的情况的示例(在本例中):forkexecps -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 [email protected] "pkill -f asdf || true"sshdbash -c "pkill -f asdf || true"

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

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

答案2

您的远程命令会自行终止:

$ 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

答案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" ...

相关内容