當我嘗試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"
1
pkill
沒有符合的進程」。
具有挑戰性的部分是理解為什麼運行時 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 步驟。以下是執行遠端簡單命令時發生的情況的範例(在本例中):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 [email protected] "pkill -f asdf || true"
sshd
bash -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" ...