2009-09-17 7 views
13

私はcronから5分ごとに実行したいbashスクリプトを持っています...しかし、スクリプトの前回の実行はまだ行われていない可能性があります。この場合は、新しい実行を終了してください。私は/ tmpのロックファイルに依存したくありません。ロックファイル(または何でも)を尊重する前にプロセスが実際に実行されていることを確認したいのですが...bashスクリプトがまだ実行されていないことを確認するにはどうすればよいですか?

ここにありますこれまでのインターネットから盗まれた...どうすればそれを少しスマートにすることができますか?それとも全く違うやり方がありますか?

if [ -f /tmp/mylockFile ] ; then 
    echo 'Script is still running' 
else 
    echo 1 > /tmp/mylockFile 
    /* Do some stuff */ 
    rm -f /tmp/mylockFile 
fi 
+0

可能重複[一度に1つのシェルスクリプトのインスタンスしか確実に実行されないようにするためのクイックアンドダーティな方法](http://stackoverflow.com/questions/185451/quick-and-dirty-way-to-ensure-only- shell-script-is-running-at) – user

答えて

21
# Use a lockfile containing the pid of the running process 
# If script crashes and leaves lockfile around, it will have a different pid so 
# will not prevent script running again. 
# 
lf=/tmp/pidLockFile 
# create empty lock file if none exists 
cat /dev/null >> $lf 
read lastPID < $lf 
# if lastPID is not null and a process with that pid exists , exit 
[ ! -z "$lastPID" -a -d /proc/$lastPID ] && exit 
echo not running 
# save my pid in the lock file 
echo $$ > $lf 
# sleep just to make testing easier 
sleep 5 

このスクリプトには少なくとも1つの競合状態があります。人生支援システムLOLには使用しないでください。しかし、あなたの環境は2つのスクリプトを同時に開始しないので、あなたの例でうまくいくはずです。より多くのアトミックロックを使用する方法はたくさんありますが、一般的には、オプションでインストールするか、NFSなどで別々に動作させるかによって異なります。

+1

なぜ 'touch $ lf'ではなく' cat/dev/null >> $ lf'ですか?ループがないときになぜ「スリープ」するのですか? –

+0

'touch(1)'も良いです。睡眠は私のスクリプトの断片を自分自身でテスト可能にするためのデモンストレーションのためのものです。 – DigitalRoss

+1

(つまり、スクリプトが終了するとすぐにPIDが消えます) – DigitalRoss

4

あなたは、プロセスの存在を確認したい場合は、単に

ps aux | grep your_script_name

の出力を見て、それがある場合、それは死んでいないのです...

としてはで指摘しましたコメント、およびその他の答えは、ロックファイルに格納されたPIDを使用する方がはるかに安全で、ほとんどのアプリが採用する標準的なアプローチです。便利ですし、実際にはコーナーケース(たとえば、cronが実行されたときにファイルを編集するなど)がほとんど見られません。

+1

さらに、現在のBASHプロセスのPIDをそのロックファイルに格納することができます。 BASHは、その番号を与える変数 '$$'(引用符を引いたもの)を提供します。こちらをご覧ください:http://tldp.org/LDP/abs/html/internalvariables。これらの変数の一部のhtml – Buggabill

+0

-1「emacs your_script_name」が実行中のプロセスの1つである場合はどうなりますか? – mob

+0

あなたはどちらも絶対に正しいです。ファイルに格納されているPIDははるかに良い方法です。私は怠け者で、スクリプトはミッションクリティカルなものではなく、通常は動作します(実際にはcronが実行されるときにスクリプトを編集することは非常にまれです)ので、このようにする傾向があります。 –

4

あなたのpidをmylockFileに保存してください。チェックする必要があるときは、ファイルから読み込んだpidを持つプロセスのpsを調べます。存在する場合、スクリプトは実行中です。

0

あなたはいつもちょうどすることができます

if ps -e -o cmd | grep scriptname > /dev/null; then 
    exit 
fi 

しかし、私はロックファイルを自分で好きなので、私もロックファイルなしでこれをしないだろう。

+1

移植性が必要な場合は、psオプションを微調整する必要があるかもしれませんが、/ dev/nullの代わりに-qをgrepに追加することができます –

+2

十分な同僚証明はありません:スクリプトレスを実行するのを防ぐには十分でしょう。 – mob

+0

mobrule:ああ、決してそれを考えなかったのは、私がこのようにしないからです.--)ロックファイルが優れています。 –

9

マニュアルページflockコマンドを使用してください。

 
NAME 
     flock - Manage locks from shell scripts 
SYNOPSIS 
     flock [-sxon] [-w timeout] lockfile [-c] command... 
+0

この解決策は、他の提案されているソリューションとは異なり、ロックの確認と取得の競合状態を避けることができます。あなたが 'flock(1)'(debian/Ubuntuなどのutil-linuxに付属しています)を使っているなら、間違いなくそれを使用してください。 OPの使用例では、短いタイムアウト(たとえば-w 1)が必要です。コマンドがすでに実行されている場合、flockは1秒後に放棄され、コマンドを実行しません。それ以外の場合は、ロックを取得してコマンドを開始します。 – arielf

+0

タイムアウトして1秒後に終了するために '-w 1'を使用する代わりに、ロックが即時に実行できない場合、OPは' -n、--nb、--nonblock Fail(終了コードは1)このトピックがアクティブになった後に悲しそうに追加された私の答えの例を見てください。 – mattst

1

場合によっては、スクリプトを実行している人を区別し、並行性を許可することができますが、すべてを許可することはできません。この場合、ユーザー単位、tty単位、またはcron固有のロックを使用できます。

$ USERなどの環境変数やttyなどのプログラムの出力を使用してファイル名を作成できます。 cronの場合は、crontabファイルに変数を設定し、スクリプトでその変数をテストできます。

6

ロックを使用しないファイルは常にロックディレクトリを使用します。 特定のケースでは、スクリプトの開始が5分間隔でスケジュールされるため、それほど重要ではありません。しかし、WebサーバーのCGIスクリプトにこのコードを再利用した場合、あなたはトーストしています。

これは、古いロックがある場合、ロックは存在しますが関連付けられたプロセスは存在しないことを意味します。あなたのクーロンは決して走りません。

なぜディレクトリを使用するのですか? mkdirはアトミックな操作であるためです。一度に1つのプロセスだけがディレクトリを作成できますが、他のすべてのプロセスはエラーになります。これは、共有ファイルシステムでも、おそらく異なるOSタイプ間でさえも機能します。

+0

ロックファイルはNFS経由では動作しません。これは共有ホスティングでは一般的です。同じ理由でロックディレクトリを使用することがよくあります。 –

3

ロックファイルを使用する場合は、ロックファイルが常に削除されていることを確認する必要があります。 'trap'でこれを行うことができます:

if (set -o noclobber; echo "locked" > "$lockfile") 2> /dev/null; then 
    trap 'rm -f "$lockfile"; exit $?' INT TERM EXIT 
    echo "Locking succeeded" >&2 
    rm -f "$lockfile" 
else 
    echo "Lock failed - exit" >&2 
    exit 1 
fi 

noclobberオプションを使用すると、ディレクトリを使用するようにアトミックなロックファイルを作成できます。

0

あなたが使用することができ、この1:

pgrep -f "/bin/\w*sh .*scriptname" | grep -vq $$ && exit 
3
ワンライナーとして

、あなたがロックファイル(例えばB/C /読み取り専用のファイルシステムなどの)

test "$(pidof -x $(basename $0))" != $$ && exit 
を使用しない場合

スクリプトの名前を持つPIDの完全なリストが現在のPIDと等しいことを確認します。 "-x"は、シェルスクリプトの名前もチェックします。

バッシュは、それも短く、より速くなります:

[[ "$(pidof -x $(basename $0))" != $$ ]] && exit 
1

私は今日、この問題を解決しようとしていたと私は以下を思い付いた:

​​

これはPIDが含まれている$BASHPIDに依存していますサブシェルの内側にあります(サブシェルの$$は親のpidです)。しかし、これはBash v4に依存しており、私はこれをBash v3.2.48を持つOSX上で実行する必要がありました。私は最終的には別の解決策を思い付いた、それがクリーンである:

JOBS=$(sh -c "ps axo pid,command | grep \"${COMMAND_LINE}\" | grep -v grep | grep -v $$") 
0

ソケット・ソリューションは、まだそれはソケットが効果的なミューテックスとして使用することができることを指摘する価値がある言及されていなかったので。ソケットの作成は原子操作であり、mkdirはGunstickが指摘しているように、ソケットはロックまたはmutexとして使用するのに適しています。

Tim KayのPerlスクリプト 'Solo'は、スクリプトのコピーを一度に1つしか実行できないようにするための非常に小さく効果的なスクリプトです。これはcronジョブ用に特別に設計されていますが、他のタスクでもうまく動作しますが、非crobジョブでも非常に効果的です。

Soloは、スクリプトの外部でチェックが実行されるという点で、これまで説明した他の手法よりも1つの利点があります。スクリプトがすでに実行されている場合、そのスクリプトの2番目のインスタンスは決して起動されません。これは、ロックで保護されているスクリプト内のコードブロックを分離するのとは対照的です。編集:flockがスクリプト内からではなくcronジョブで使用されている場合は、それを使用してスクリプトの2番目のインスタンスが開始されないようにすることもできます。下記の例を参照してください。ここで

はあなたのcronでそれを使用する方法の例です:

*/5 * * * * solo -port=3801 /path/to/script.sh args args args 

# "/path/to/script.sh args args args" is only called if no other instance of 
# "/path/to/script.sh" is running, or more accurately if the socket on port 3801 
# is not open. Distinct port numbers can be used for different programs so that 
# if script_1.sh is running it does not prevent script_2.sh from starting, I've 
# used the port range 3801 to 3810 without conflicts. For Linux non-root users 
# the valid port range is 1024 to 65535 (0 to 1023 are reserved for root). 

* * * * * solo -port=3802 /path/to/script_1.sh 
* * * * * solo -port=3803 /path/to/script_2.sh 

# Flock can also be used in cron jobs with a distinct lock path for different 
# programs, in the example below script_3.sh will only be started if the one 
# started a minute earlier has already finished. 

* * * * * flock -n /tmp/path.to.lock -c /path/to/script_3.sh 

リンク:

・ホープこれは役に立ちます。

0

thisを使用できます。

私はちょうどここに解決策をコピーして貼り付けます。それは両方の質問に対する答えです(私は実際にはこの質問に適していると主張します)。

が使用

  1. sh_lock_functions.sh
  2. INIT使用sh_lock_init
  3. ロック用いsh_acquire_lock
  4. sh_check_lockを使用してチェックロック
  5. 国連を含みます

スクリプトファイルsh_remove_lockを使用してロック

sh_lock_functions.sh

#!/bin/bash 

function sh_lock_init { 
    sh_lock_scriptName=$(basename $0) 
    sh_lock_dir="/tmp/${sh_lock_scriptName}.lock" #lock directory 
    sh_lock_file="${sh_lock_dir}/lockPid.txt" #lock file 
} 

function sh_acquire_lock { 
    if mkdir $sh_lock_dir 2>/dev/null; then #check for lock 
     echo "$sh_lock_scriptName lock acquired successfully.">&2 
     touch $sh_lock_file 
     echo $$ > $sh_lock_file # set current pid in lockFile 
     return 0 
    else 
     touch $sh_lock_file 
     read sh_lock_lastPID < $sh_lock_file 
     if [ ! -z "$sh_lock_lastPID" -a -d /proc/$sh_lock_lastPID ]; then # if lastPID is not null and a process with that pid exists 
      echo "$sh_lock_scriptName is already running.">&2 
      return 1 
     else 
      echo "$sh_lock_scriptName stopped during execution, reacquiring lock.">&2 
      echo $$ > $sh_lock_file # set current pid in lockFile 
      return 2 
     fi 
    fi 
    return 0 
} 

function sh_check_lock { 
    [[ ! -f $sh_lock_file ]] && echo "$sh_lock_scriptName lock file removed.">&2 && return 1 
    read sh_lock_lastPID < $sh_lock_file 
    [[ $sh_lock_lastPID -ne $$ ]] && echo "$sh_lock_scriptName lock file pid has changed.">&2 && return 2 
    echo "$sh_lock_scriptName lock still in place.">&2 
    return 0 
} 

function sh_remove_lock { 
    rm -r $sh_lock_dir 
} 

使用例

sh_lock_usage_example.sh

#!/bin/bash 
. /path/to/sh_lock_functions.sh # load sh lock functions 

sh_lock_init || exit $? 

sh_acquire_lock 
lockStatus=$? 
[[ $lockStatus -eq 1 ]] && exit $lockStatus 
[[ $lockStatus -eq 2 ]] && echo "lock is set, do some resume from crash procedures"; 

#monitoring example 
cnt=0 
while sh_check_lock # loop while lock is in place 
do 
    echo "$sh_scriptName running (pid $$)" 
    sleep 1 
    let cnt++ 
    [[ $cnt -gt 5 ]] && break 
done 

#remove lock when process finished 
sh_remove_lock || exit $? 

exit 0 

特長

  • は、プロセスがすでにスクリプトがロックの除去(例えば前に停止した場合は、検出することができ
  • を実行していないことを確認するためにロックしたファイル、ディレクトリとプロセスIDの組み合わせを使用します。プロセスのkill、シャットダウン、エラーなど)
  • あなたがロックファイルをチェックし、ロックが
  • 冗長が欠落しているときに、プロセスのシャットダウンをトリガするためにそれを使用することができますが、簡単にデバッグのためにエラーメッセージを出力し
関連する問題