在 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" ...

相關內容