web-dev-qa-db-ja.com

bashスクリプトの1つのインスタンスのみが実行されることを確認するにはどうすればよいですか?

追加のツールを必要としないソリューションが推奨されます。

31
Tobias Kienzler

Nsgの答えとほぼ同じです。ロックを使用しますdirectory。ディレクトリの作成は、linuxとunix、* BSD、および他の多くのOSでアトミックです。

if mkdir $LOCKDIR
then
    # Do important, exclusive stuff
    if rmdir $LOCKDIR
    then
        echo "Victory is mine"
    else
        echo "Could not remove lock dir" >&2
    fi
else
    # Handle error condition
    ...
fi

デバッグのために、ロックshのPIDをロックディレクトリ内のファイルに入れることができますが、そのPIDをチェックしてロックプロセスがまだ実行されているかどうかを確認できるという考えの罠に陥らないでください。多くの競合状態がその道を横切っています。

18
Bruce Ediger

Bruce Edigerの回答 に追加し、 この回答 に触発された場合は、スクリプトの終了を防ぐためにクリーンアップにスマートを追加する必要があります。

#Remove the lock directory
function cleanup {
    if rmdir $LOCKDIR; then
        echo "Finished"
    else
        echo "Failed to remove lock directory '$LOCKDIR'"
        exit 1
    fi
}

if mkdir $LOCKDIR; then
    #Ensure that if we "grabbed a lock", we release it
    #Works for SIGTERM and SIGINT(Ctrl-C)
    trap "cleanup" EXIT

    echo "Acquired lock, running"

    # Processing starts here
else
    echo "Could not create lock directory '$LOCKDIR'"
    exit 1
fi
18
Igor Zevaka

これは単純すぎる可能性があります。間違っている場合は修正してください。単純なpsでは不十分ですか?

#!/bin/bash 

me="$(basename "$0")";
running=$(ps h -C "$me" | grep -wv $$ | wc -l);
[[ $running > 1 ]] && exit;

# do stuff below this comment
6
terdon

Bashスクリプトの単一のインスタンスが実行されることを確認するもう1つの方法:

#!/bin/bash

# Check if another instance of script is running
pidof -o %PPID -x $0 >/dev/null && echo "ERROR: Script $0 already running" && exit 1

...

pidof -o %PPID -x $0は、既存のスクリプトが既に実行されている場合はそのPIDを取得し、他のスクリプトが実行されていない場合はエラーコード1で終了します。

5
Sethu

追加のツールなしで解決策を求めてきましたが、これがflockを使用する私のお気に入りの方法です。

#!/bin/sh

[ "${FLOCKER}" != "$0" ] && exec env FLOCKER="$0" flock -en "$0" "$0" "$@" || :

echo "servus!"
sleep 10

これはman flockの例のセクションから来ており、さらに説明しています:

これは、シェルスクリプトの有用なボイラープレートコードです。ロックするシェルスクリプトの先頭に配置すると、最初の実行時に自動的にロックされます。 env var $ FLOCKERが実行中のシェルスクリプトに設定されていない場合は、flockを実行し、(スクリプト自体をロックファイルとして使用して)排他的な非ブロックロックを取得してから、正しい引数で自身を再実行します。また、FLOCKER env varを正しい値に設定して、再度実行されないようにします。

考慮すべき点:

  • flockが必要です。サンプルスクリプトが見つからない場合、エラーで終了します。
  • 追加のロックファイルは不要
  • スクリプトがNFS上にある場合は機能しない可能性があります( https://serverfault.com/questions/66919/file-locks-on-an-nfs を参照)

参照 https://stackoverflow.com/questions/185451/quick-and-dirty-way-to-ensure-only-one-instance-of-a-Shell-script-is-running-at

5
schieferstapel

マルコが言ったように、私はロックファイルを使います

#!/bin/bash

# Exit if /tmp/lock.file exists
[ -f /tmp/lock.file ] && exit

# Create lock file, sleep 1 sec and verify lock
echo $$ > /tmp/lock.file
sleep 1
[ "x$(cat /tmp/lock.file)" == "x"$$ ] || exit

# Do stuff
sleep 60

# Remove lock file
rm /tmp/lock.file
4
nsg

スクリプトのインスタンスが1つだけ実行されていることを確認したい場合は、以下をご覧ください。

スクリプトをロック(並列実行に対して)

それ以外の場合は、psを確認するか、lsof <full-path-of-your-script>を呼び出すことができます。追加のツールを呼び出さないためです。


補足

実際に私はこのようにすることを考えました:

for LINE in `lsof -c <your_script> -F p`; do 
    if [ $$ -gt ${LINE#?} ] ; then
        echo "'$0' is already running" 1>&2
        exit 1;
    fi
done

これにより、<your_script>の複数のインスタンスを同時にフォークして実行した場合でも、pidが最も低いプロセスのみが実行を継続することが保証されます。

3
user1146332

私はcksumを使用して、スクリプトが本当に1つのインスタンスを実行していることを確認しています。ファイル名とファイルパスを変更してもです。

トラップ&ロックファイルを使用していません。サーバーが突然ダウンした場合、サーバーが起動した後に手動でロックファイルを削除する必要があるためです。

注:最初の行の#!/ bin/bashはgrep psに必要です

#!/bin/bash

checkinstance(){
   nprog=0
   mysum=$(cksum $0|awk '{print $1}')
   for i in `ps -ef |grep /bin/bash|awk '{print $2}'`;do 
        proc=$(ls -lha /proc/$i/exe 2> /dev/null|grep bash) 
        if [[ $? -eq 0 ]];then 
           cmd=$(strings /proc/$i/cmdline|grep -v bash)
                if [[ $? -eq 0 ]];then 
                   fsum=$(cksum /proc/$i/cwd/$cmd|awk '{print $1}')
                   if [[ $mysum -eq $fsum ]];then
                        nprog=$(($nprog+1))
                   fi
                fi
        fi
   done

   if [[ $nprog -gt 1 ]];then
        echo $0 is already running.
        exit
   fi
}

checkinstance 

#--- run your script bellow 

echo pass
while true;do sleep 1000;done

または、スクリプト内でcksumをハードコーディングできるので、スクリプトのファイル名、パス、または内容を変更する場合でも、心配する必要はありません

#!/bin/bash

mysum=1174212411

checkinstance(){
   nprog=0
   for i in `ps -ef |grep /bin/bash|awk '{print $2}'`;do 
        proc=$(ls -lha /proc/$i/exe 2> /dev/null|grep bash) 
        if [[ $? -eq 0 ]];then 
           cmd=$(strings /proc/$i/cmdline|grep -v bash)
                if [[ $? -eq 0 ]];then 
                   fsum=$(grep mysum /proc/$i/cwd/$cmd|head -1|awk -F= '{print $2}')
                   if [[ $mysum -eq $fsum ]];then
                        nprog=$(($nprog+1))
                   fi
                fi
        fi
   done

   if [[ $nprog -gt 1 ]];then
        echo $0 is already running.
        exit
   fi
}

checkinstance

#--- run your script bellow

echo pass
while true;do sleep 1000;done
2
arputra

これは Anselmo's Answer の修正版です。アイデアは、bashスクリプト自体を使用して読み取り専用ファイル記述子を作成し、flockを使用してロックを処理することです。

SCRIPT=`realpath $0`     # get absolute path to the script itself
exec 6< "$SCRIPT"        # open bash script using file descriptor 6
flock -n 6 || { echo "ERROR: script is already running" && exit 1; }   # lock file descriptor 6 OR show error message if script is already running

echo "Run your single instance code here"

他のすべての回答との主な違いは、このコードはファイルシステムを変更せず、フットプリントが非常に低く、スクリプトが終了状態に関係なく終了するとすぐにファイル記述子が閉じられるため、クリーンアップを必要としないことです。したがって、スクリプトが失敗したか成功したかは関係ありません。

2
John Doe

これを使用できます: https://github.com/sayanarijit/pidlock

Sudo pip install -U pidlock

pidlock -n sleepy_script -c 'sleep 10'
0
Arijit Basu

あなたへの私のコード

#!/bin/bash

script_file="$(/bin/readlink -f $0)"
lock_file=${script_file////_}

function executing {
  echo "'${script_file}' already executing"
  exit 1
}

(
  flock -n 9 || executing

  sleep 10

) 9> /var/lock/${lock_file}

に基づく man flock、改善のみ:

  • スクリプトの完全な名前に基づく、ロックファイルの名前
  • メッセージexecuting

ここにsleep 10、すべてのメインスクリプトを配置できます。

この便利なパッケージは、あなたが探しているものを実行します。

https://github.com/krezreb/singleton

インストールしたら、コマンドの前にsingleton LOCKNAMEを付けます。

例えばsingleton LOCKNAME PROGRAM ARGS...

0
Joseph Beeson