
更新:
我已將腳本中的部分更改grep $1
為grep '$1'
(當我試圖表示時),這次我得到了grep "$1"
kill: usage: kill [-s sigspec | -n signum | -sigspec] pid | jobspec ... or kill -l [sigspec]
訊息(而不是Terminated: 15
訊息)。我不明白發生了什麼事。
問題:
我寫了一個簡單的 shell 腳本,名為mykill
.
mykill
:
#!/bin/sh
kill `ps -A | grep $1 | grep -v 'grep' | grep -Eom 1 '^[0-9]+'`
然而,有一個奇怪的行為。當我寫下這一行時:
kill `ps -A | grep process_name | grep -v 'grep' | grep -Eom 1 '^[0-9]+'`
手動在 bash 上,如果沒有任何輸出ps -A | grep process_name
,我會得到以下內容:
kill: usage: kill [-s sigspec | -n signum | -sigspec] pid | jobspec ... or kill -l [sigspec]
如果出現問題,該命令將正確執行並靜默終止。
現在,如果我透過執行檔來執行腳本mykill
,如果 的輸出出現某些內容ps -A | grep process_name
,則腳本會正確執行並靜默終止,這與手動執行命令的行為相同。
但是,如果 的輸出沒有出現任何內容ps -A | grep process_name
,我就不會收到有關使用kill 命令的訊息。相反,我得到:
Terminated: 15
我還檢查了返回碼。當我嘗試透過在 shell 上手動編寫命令來終止不存在的進程後,echo $?
給出1
.但是,在我嘗試透過呼叫腳本終止不存在的進程後,echo $?
給了143
.
這裡發生了什麼事?為什麼我在透過手動在 shell 上編寫的方式執行相同的命令時與在 shell 腳本中執行它時觀察到不同的行為?
注意:sh
我的工作 shell 都是bash
.
獎勵:我的 shell 腳本能否以更有效和/或更優雅的方式編寫,僅使用POSIX 實用程式?如果是這樣,怎麼辦?
答案1
可移植性說明。的輸出格式ps -A
為POSIX 未指定對於不符合 Unix 的系統(如 FreeBSD)(您會注意到輸出格式部分和選項的描述-f
都被標記為XSI在規範中),所以你不能真正可靠地對其進行後處理。
例如,對於Linux 上的ps
from procps
,它將輸出PID TTY TIME CMD
columns(其中CMD
是進程名稱,而不是命令參數),而在 FreeBSD 上它會輸出PID TT STAT TIME COMMAND
(其中COMMAND
是參數)。
鑑於您的使用情況grep -v grep
,我想您期望後者或至少ps -A
輸出進程執行的命令的參數,而不僅僅是進程名稱(通常源自最後一個執行命令或第一個(第0個)參數)。
如果您grep
只想grep
使用命令參數,則應該使用:
ps -A -o pid= -o args=
其輸出由 POSIX 指定。
現在,你的問題是它mykill
正在自殺,因為mykill foo
匹配foo
.
另一個問題是它mykill grep
不會殺死任何東西。
在這裡,你可以這樣做:
#! /bin/sh -
PATTERN=${1?} export PATTERN
trap '' TERM # ignore SIGTERM for the shell and its children
ps -A -o pid= -o args= | awk '$0 ~ ENVIRON["PATTERN"] {
system("kill " $1); exit}'
(請注意,POSIX 不指定 POSIX 實用程式的路徑,sh
也不指定 she-bang 機制,因此/bin/sh
可能不是 POSIX shell。但實際上,大多數 POSIX 系統都支援 she-bang,並且/bin/sh
是 POSIXsh
或 Bournesh
並且上面的程式碼應該適用於兩者)。
儘管這並不理想,因為即使沒有找到進程,它總是會傳回 true (0) 退出狀態。更好的方法是:
#! /bin/sh -
pattern=${1?}
trap '' TERM # ignore SIGTERM for the shell and its children
ps -A -o pid= -o args= | grep -e "$pattern" | {
read pid args && kill "$pid"
}
grep -m 1
在這兩種情況下,我們只會按照您的方法建議的方式終止第一個匹配進程。
現在,trap '' SIGTERM
我們確保我們的進程沒有被殺死,如果我們要殺死它就可以了全部匹配的進程,但因為在這裡,我們只殺死第一個匹配的進程,問題是第一個很可能是正在運行的進程mykill pattern
或grep pattern
。
grep -ve grep -e mykill
您可以嘗試比較匹配進程的進程 ID,而不是添加一些進程(這並不是萬無一失的,因為它可能會排除比預期更多的進程)。
#! /bin/sh -
pattern=${1?}
trap '' TERM # ignore SIGTERM for the shell and its children
# just in case
psoutput=$(exec ps -A -o pid= -o ppid= -o args=)
printf '%s\n' "$psoutput" | grep -e "$pattern" | {
while read -r pid ppid args; do
if [ "$pid" -ne "$$" ] && [ "$ppid" -ne "$$" ]; then
kill "$pid"
exit # with the exit status of kill above
fi
done
exit 1 # not found
}
(請注意,$(...)
和read -r
是 POSIX,但不是 Bourne)。
或使用ksh93
, bash
, zsh
or yash
(都不是 POSIX 指令),這是一個具有內建正規表示式匹配的 shell:
#! /bin/bash -
pattern=${1?}
trap '' TERM # ignore SIGTERM for the shell and its children
# just in case
psoutput=$(exec ps -A -o pid= -o ppid= -o args=)
printf '%s\n' "$psoutput" | {
while read -r pid ppid args; do
if ((pid != $$ && ppid != $$)) && [[ $args =~ $pattern ]]; then
kill "$pid"
exit # with the exit status of kill above
fi
done
exit 1 # not found
}