web-dev-qa-db-ja.com

インスタンスの実行数をカウントするBashスクリプトが機能しない

Linuxマシンですでに実行されているインスタンスがあるかどうかを検出し、インスタンスの数を画面に表示するスクリプトを作成しています。

「detect_itself.sh」スクリプトの内容は次のとおりです。

#!/bin/sh

INSTANCES_NUMBER=`ps -ef | grep detect_itself.sh | grep -v -i grep | wc -l`
echo "Number of detect_itself.sh instances running now =" $INSTANCES_NUMBER
echo "Second method:"
ps -ef | grep detect_itself.sh | grep -v -i grep | wc -l
echo "Third method:"
echo `ps -ef | grep detect_itself.sh | grep -v -i grep | wc -l`
echo "Please, press a key"
read -r key

スクリプトを実行すると、画面ごとに表示されます。

Number of detect_itself.sh instances running now = 2
Second method:
1
Third method:
2
Please, press a key

しかし、私はそれが示すことを期待していました:

Number of detect_itself.sh instances running now = 1
Second method:
1
Third method:
1
Please, press a key

ps -ef | grep detect_itself.sh | grep -v -i grep | wc -lを実行すると、値1が返される理由がわかりませんが、この値を変数に保存してエコーで表示すると、2が表示されます。

2
jstechg

これは、サブシェルでpsコマンドを実行しているために発生しています。これを実行すると:

INSTANCES_NUMBER=`ps -ef | grep detect_itself.sh | grep -v -i grep | wc -l`

これは実際に、そのコマンドを実行するための新しいサブシェルをフォークします。このフォークは親のコピーであるため、2つのdetect_itself.shインスタンスが実行されています。説明のために、これを実行します。

#!/bin/sh
echo "Running the ps command directly:"
ps -ef | grep detect_itself.sh | grep -v -i grep
echo "Running the ps command in a subshell:"
echo "`ps -ef | grep detect_itself.sh | grep -v -i grep`"

印刷する必要があります:

$ test.sh
Running the ps command directly:
terdon   25683 24478  0 14:58 pts/11   00:00:00 /bin/sh /home/terdon/scripts/detect_itself.sh
Running the ps command in a subshell:
terdon   25683 24478  0 14:58 pts/11   00:00:00 /bin/sh /home/terdon/scripts/detect_itself.sh
terdon   25688 25683  0 14:58 pts/11   00:00:00 /bin/sh /home/terdon/scripts/detect_itself.sh

幸いなことに、そのためのアプリがあります!この種のことがまさにpgrepが存在する理由です。したがって、スクリプトを次のように変更します。

#!/bin/sh
instances=`pgrep -fc detect_itself.sh`
echo "Number of detect_itself.sh instances running now = $instances"
echo "Second method:"
ps -ef | grep detect_itself.sh | grep -v -i grep | wc -l
echo "Third method (wrong):"
echo `ps -ef | grep detect_itself.sh | grep -v -i grep | wc -l`

印刷する必要があります:

$ detect_itself.sh
Number of detect_itself.sh instances running now = 1
Second method:
1
Third method (wrong):
2

[〜#〜]重要[〜#〜]:これは安全な方法ではありません。たとえば、this_will_detect_itselfというスクリプトがある場合、それはカウントされます。ファイルをテキストエディタで開いている場合は、それもカウントされます。この種のことに対するはるかに堅牢なアプローチは、ロックファイルを使用することです。何かのようなもの:

#!/bin/sh

if [[ -e /tmp/I_am_running ]]; then
    echo "Already running! Will exit."
    exit
else
    touch /tmp/I_am_running
fi
## do whatever you want to do here

## remove the lock file at the end
rm /tmp/I_am_running

または、さらに良いことに、trapを使用して、スクリプトがクラッシュした場合でもファイルが削除されていることを確認してください。詳細は、実行中のインスタンスを検出する必要がある理由に応じて、正確に何をしたいかによって異なります。

1
terdon