
修正する:
grep $1
台本の部分を(表現しようとしたとき)に変更しましたが、grep '$1'
今回はgrep "$1"
kill: usage: kill [-s sigspec | -n signum | -sigspec] pid | jobspec ... or kill -l [sigspec]
メッセージ(メッセージではありませんTerminated: 15
)。私は何が起こっているのか分かりません。
質問:
私はという単純なシェルスクリプトを書いています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
返品コードも確認してみました。シェルにコマンドを手動で作成し、存在しないプロセスを終了しようとした後にecho $?
返されます1
。しかし、スクリプトを呼び出して存在しないプロセスをecho $?
終了しようとすると143
。
ここで何が起こっているのでしょうか?同じコマンドをシェルスクリプトで実行するときと、シェルに手動で作成して実行するときに異なる動作が観察されるのはなぜですか?
注:sh
私の作業シェルはすべてbash
。
ボーナス:私のシェルスクリプトを以下を使用してより効率的でエレガントな方法で作成できますか?POSIXユーティリティ?それでは、どうすればいいですか?
答え1
移植性に関する注意事項。出力形式ps -A
はPOSIX が指定されていません。Unixと互換性のないシステム(FreeBSDなど)の場合(出力フォーマットセクションとオプションの説明が表示されていること-f
がわかります)XSI仕様から)実際に安定して後処理することはできません。
ps
たとえば、from Linuxでは列(ここではコマンドパラメータではなくプロセス名)を印刷しprocps
、FreeBSDは印刷(ここでパラメータ)を印刷します。PID TTY TIME CMD
CMD
PID TT STAT TIME COMMAND
COMMAND
あなたのユースケースを考えると、プロセス名(通常は最後に実行されたコマンドまたは最初の(0番目)引数から派生したもの)だけでなく、grep -v grep
後者を期待するか、少なくともps -A
プロセスで実行されたコマンドの引数を出力することが期待されます。
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
メカニズムを指定していないため、POSIXシェルではない/bin/sh
かもしれませんが、実際にはほとんどのPOSIXシステムはシェバンをサポートしており、POSIXまたは/bin/sh
Bournesh
にすることができますsh
。すべての枝に対して機能する必要があります。
ただし、これはプロセスが見つからなくても常に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ではありません)。
または、正規表現マッチング機能が組み込まれているシェル、または(POSIXコマンドでもない)をksh93
使用bash
してくださいzsh
。yash
#! /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
}