シェルスクリプトはシェルとは異なる動作をしますか?

シェルスクリプトはシェルとは異なる動作をしますか?

修正する:

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_namekill コマンドの使用に関するメッセージは表示されません。代わりに、私は次のようになります。

Terminated: 15

返品コードも確認してみました。シェルにコマンドを手動で作成し、存在しないプロセスを終了しようとした後にecho $?返されます1。しかし、スクリプトを呼び出して存在しないプロセスをecho $?終了しようとすると143

ここで何が起こっているのでしょうか?同じコマンドをシェルスクリプトで実行するときと、シェルに手動で作成して実行するときに異なる動作が観察されるのはなぜですか?

注:sh私の作業シェルはすべてbash

ボーナス:私のシェルスクリプトを以下を使用してより効率的でエレガントな方法で作成できますか?POSIXユーティリティ?それでは、どうすればいいですか?

ベストアンサー1

移植性に関する注意事項。出力形式ps -APOSIX が指定されていません。Unixと互換性のないシステム(FreeBSDなど)の場合(出力フォーマットセクションとオプションの説明が表示されていること-fがわかります)XSI仕様から)実際に安定して後処理することはできません。

psたとえば、from Linuxでは列(ここではコマンドパラメータではなくプロセス名)を印刷しprocps、FreeBSDは印刷(ここでパラメータ)を印刷します。PID TTY TIME CMDCMDPID TT STAT TIME COMMANDCOMMAND

あなたのユースケースを考えると、プロセス名(通常は最後に実行されたコマンドまたは最初の(0番目引数から派生したもの)だけでなく、grep -v grep後者を期待するか、少なくともps -Aプロセスで実行されたコマンドの引数を出力することが期待されます。

grepコマンドパラメーターのみを使用するには、grep以下を使用する必要があります。

ps -A -o pid= -o args=

出力は POSIX によって指定されます。

今、あなたの問題は、それが一致のmykillために自分自身を殺しているということです。mykill foofoo

もう一つの問題は、何も殺さないことです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/shBourneshにすることができます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 -rPOSIXですがBourneではありません)。

または、正規表現マッチング機能が組み込まれているシェル、または(POSIXコマンドでもない)をksh93使用bashしてくださいzshyash

#! /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
}

おすすめ記事