更新:
スクリプトで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ユーティリティ のみを使用して、より効率的および/またはエレガントな方法で記述できますか?もしそうなら、どのように?
移植性に関する注意。 _ps -A
_の出力形式は POSIXでは指定されていません 非Unix準拠システム(FreeBSDなど)の場合(出力形式のセクションと_-f
_の説明に気付くでしょう。オプションはすべてタグ付けされています[〜#〜] xsi [〜#〜]仕様内)、したがって、移植可能に確実に後処理することはできません。
たとえば、Linuxのps
からのprocps
を使用すると、オンのときに_PID TTY TIME CMD
_列(CMD
はプロセス名であり、コマンド引数ではありません)が出力されます。 FreeBSDは_PID TT STAT TIME COMMAND
_を出力します(COMMAND
が引数です)。
_grep -v grep
_の使用法を考えると、後者を期待していると思います。少なくとも、_ps -A
_は、プロセス名(通常はファイルから派生)だけでなく、プロセスが実行したコマンドの引数を出力します。最後の実行コマンドまたは最初の実行コマンドの名前(0th)引数)。
grep
がコマンド引数のみでgrep
を対象としている場合は、次を使用する必要があります。
_ps -A -o pid= -o args=
_
その出力はPOSIXによって指定されます。
さて、あなたの問題は、_mykill foo
_がmykill
と一致するため、foo
が自殺していることです。
もう1つの問題は、_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シェルではない可能性があることに注意してください。ただし、実際には、she-bangはでサポートされています。ほとんどのPOSIXシステムと_/bin/sh
_はPOSIX sh
またはBournesh
のいずれかであり、上記のコードは両方で機能するはずです)。
プロセスが見つからない場合でも常に真(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
またはyash
(いずれもPOSIXコマンドではありません)を使用します。これは、正規表現マッチングが組み込まれたシェルです。
_#! /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
}
_