1. ホーム
  2. スクリプト・コラム
  3. リナックスシェル

シェルスクリプトが繰り返し実行される問題の解決方法

2022-01-09 12:32:41

イントロダクション

flock は、Linux システムのプロセス間で重要なリソースへの安全なアクセスを保証するファイルロックコマンドで、シェルスクリプトで論理的な相互排他性を制御するために使用することができます。

例1

既存のスクリプトa.shを読み込むと

#! /bin/bash

echo "[`date +'%Y-%m-%d %H:%M:%S'`] begin pid:$$... "

sleep 10

echo "[`date +'%Y-%m-%d %H:%M:%S'`] end pid:$$... "

端末で実行する(端末1と記す) flock -xn . /f.lock -c . /a.sh コマンドを実行すると、次のような結果になります。

[tt@ecs-centos-7 lock_test]$ flock -xn . /f.lock -c . /a.sh 
[2020-12-10 10:10:45] begin pid:5359...
[2020-12-10 10:10:55] end pid:5359...

上記のコマンドを実行している間に、別の端末(端末2とする)を開いて同じコマンドを実行すると、次のような結果になります。

[tt@ecs-centos-7 lock_test]$ flock -xn . /f.lock -c . /a.sh 
[tt@ecs-centos-7 lock_test]$ 

上記のコマンドは、flock -xn . /f.lock -c . /a.sh で

  • xオプションは排他的ロックで、書き込みロックと呼ばれることもあり、これはデフォルトのオプションです
  • は、その
  • nオプションはノンブロッキングで、ロックが解放されるのを待つのではなく、ロックが取得できない場合は即座に失敗を返します
  • その
  • cオプションの後に、実行するコマンドを指定します。

ターミナル 1 で flock -xn を実行 . /f.lock -c . /a.sh コマンドで f.lock ファイルをロックし、. /a.sh コマンドを実行すると、約10秒間続きます(sleep 10文)。

ターミナル2でflock -xnしているので . /f.lock -c . /a.sh コマンドは端末 1 のコマンド実行中に実行されるので、端末 1 はまだ f.lock ファイルのロックを解放しておらず、-n オプションはノンブロッキングなので、端末 2 は f.lock ファイルのロックを待ってブロックせず、すぐにリターンします。

ターミナル 2 は、flock -x を実行すると /p {{p を返します。/f.lock -c . /a.sh コマンドを実行すると、端末 1 が f.lock ファイルロックを解放するまでブロックし、その後 f.lock ファイルロックを取得し、. /a.sh コマンドの実行を開始します。

インスタンス2

例1では、flock -xn file lock -c を実行する必要があります . /a.sh を実行し、繰り返し実行できないスクリプトにはそれぞれファイルロックを割り当て、スクリプトごとに異なるファイルロック名を使用することを保証する必要があります。

ただ単に.NETを実行する方法はありますか?/a.sh コマンドを実行するだけで、例1の機能を実現する方法はありますか?

回答 はい、あります。

a.shを少し修正すると、次のようになります。

#! /bin/bash
   
   
echo "[`date +'%Y-%m-%d %H:%M:%S'`] 1111 pid:$$. .MY_LOCK:${MY_LOCK}"
   
[ "${MY_LOCK}" ! = "$0" ] && exec env MY_LOCK="$0" flock -xn "$0" "$0" "$0" "$@"
   
echo "[`date +'%Y-%m-%d %H:%M:%S'`] begin pid:$$. .MY_LOCK:${MY_LOCK}"
   
sleep 10
echo "[`date +'%Y-%m-%d %H:%M:%S'`] end pid:$$... "

端末1が ./a.sh コマンドを実行すると、次のような出力が得られます。

{コード

端末1のコマンド実行中に、端末2が ./a.sh コマンドを実行すると、次のような出力が得られます。

[tt@ecs-centos-7 lock_test]$ . /a.sh
[2020-12-10 14:11:35] 1111 pid:5944... .MY_LOCK:
[2020-12-10 14:11:35] 1111 pid:5946... .MY_LOCK:. /a.sh
[2020-12-10 14:11:35] begin pid:5946... .MY_LOCK:. /a.sh
[2020-12-10 14:11:45] end pid:5946...

新しいa.shスクリプトは、オリジナルと比較して、4行目と6行目が新しくなっています。

4行目はログ出力

6行目の説明

$0 はスクリプトの名前で、値は . /a.sh

$@ は a.sh スクリプトに渡されたすべての引数です。

exec は現在のプロセスでその直後のコマンドを実行し、現在のスクリプトプロセスでまだ実行されていないコマンドは実行されません

[tt@ecs-centos-7 lock_test]$ . /a.sh [2020-12-10 14:11:44] 1111 pid:5976... .MY_LOCK: [2020-12-10 14:11:44] は、環境変数MY_LOCKがスクリプト名( a.sh )と同じかどうかを判断するためのものです。
と同じです。

異なる場合は、以下を実行します。 [ "${MY_LOCK}" ! = "$0" ] コマンドと {コード {コード コマンド

env MY_LOCK="$0" 環境変数MY_LOCKにスクリプト名を設定します。

env MY_LOCK="$0" というのは、実は flock -xn "$0" "$0" "$@" これは、現在のスクリプト名をファイルロックとして使用します。

例2では、./a.shコマンドを実行した後、6行目に到達した時点でMY_LOCK変数がNULLになっているので flock -xn "$0" "$0" "$@" は真

は、その

execコマンドは、未実行のコマンドを無視します。つまり、現在のシェルプロセスで6行目以降のコマンドは実行されません

が続きます。 flock -xn . /a.sh . /a.sh コマンドを実行すると、変数MY_LOCKの値が現在のスクリプト名に設定されます。/a.sh を実行し、flock -xn "$0" "$0" "$@" コマンドを実行すると、新しいサブシェルで実行されます . /a.sh を実行すると、スクリプトの後続の出力に、最初とは異なるプロセス ID が表示されるようになります。

また、flock -xn "$0" "$0" "$@" の前に env MY_LOCK="$0" を実行しているため、MY_LOCK 変数の値が .NET Framework に設定されています。/a.sh になっているため、flock -xn "$0" "$0" "$@" コマンドは ./a.sh コマンドを再実行するため
の6行目のスクリプト [ "${MY_LOCK}" ! = "$0"] が偽で、6行目のexec以降のコマンドは実行されず、7行目から最後までスクリプトが続き、8行目と12行目のログも実行が終了したことを示すものになる

要約
/{br

例1と例2では、スクリプトの繰り返し実行の問題を解決するために、主にflockコマンドを使用してファイルロックを設定する方法を2つ紹介します。 [ "${MY_LOCK}" ! = "$0" ] ステートメントを使用する場合、スクリプトを呼び出すコマンドは同じままです。

flock コマンドのオプションや使い方の詳細については、man flock 自体を参照してください。

以上、シェルスクリプトの繰り返し実行問題の解決方法の詳細ですが、シェルスクリプトの繰り返し実行問題の詳細については、BinaryDevelopの他の関連記事にもご注目ください!