特定のスクリプトの1つのインスタンスのみが実行されていることを確認するための最も単純/最良の方法は何ですか-LinuxでBashを想定していますか?
現在、私はやっています:
ps -C script.name.sh > /dev/null 2>&1 || ./script.name.sh
ただし、いくつかの問題があります。
-C
プロセス名の最初の14文字のみをチェックしますもちろん、独自のpidfile処理を記述できますが、簡単な方法があるはずです。
スクリプトがすべてのユーザーで同じ場合、lockfile
アプローチを使用できます。ロックを取得した場合は、メッセージを表示して終了します。
例として:
[Terminal #1] $ lockfile -r 0 /tmp/the.lock
[Terminal #1] $
[Terminal #2] $ lockfile -r 0 /tmp/the.lock
[Terminal #2] lockfile: Sorry, giving up on "/tmp/the.lock"
[Terminal #1] $ rm -f /tmp/the.lock
[Terminal #1] $
[Terminal #2] $ lockfile -r 0 /tmp/the.lock
[Terminal #2] $
/tmp/the.lock
が取得されると、実行にアクセスできるのはスクリプトのみになります。完了したら、ロックを解除します。スクリプト形式では、これは次のようになります。
#!/bin/bash
lockfile -r 0 /tmp/the.lock || exit 1
# Do stuff here
rm -f /tmp/the.lock
アドバイザリロックは古くから使用されており、bashスクリプトで使用できます。シンプルなflock
(util-linux[-ng]
)lockfile
から(procmail
から)。そして、終了時のトラップについて常に覚えておいてください(sigspec == EXIT
または0
、これらのスクリプトでは特定の信号をトラップすることは不要です。
2009年に、ロック可能なスクリプトボイラープレートをリリースしました(当初はwikiページで利用可能でしたが、最近では Gist として利用可能です)。これをユーザーごとに1つのインスタンスに変換するのは簡単です。また、ロックまたは同期を必要とする他のシナリオ用のスクリプトを簡単に作成できます。
便宜上、前述の定型文を示します。
#!/bin/bash
# SPDX-License-Identifier: MIT
## Copyright (C) 2009 Przemyslaw Pawelczyk <[email protected]>
##
## This script is licensed under the terms of the MIT license.
## https://opensource.org/licenses/MIT
#
# Lockable script boilerplate
### HEADER ###
LOCKFILE="/var/lock/`basename $0`"
LOCKFD=99
# PRIVATE
_lock() { flock -$1 $LOCKFD; }
_no_more_locking() { _lock u; _lock xn && rm -f $LOCKFILE; }
_prepare_locking() { eval "exec $LOCKFD>\"$LOCKFILE\""; trap _no_more_locking EXIT; }
# ON START
_prepare_locking
# PUBLIC
exlock_now() { _lock xn; } # obtain an exclusive lock immediately or fail
exlock() { _lock x; } # obtain an exclusive lock
shlock() { _lock s; } # obtain a shared lock
unlock() { _lock u; } # drop a lock
### BEGIN OF SCRIPT ###
# Simplest example is avoiding running multiple instances of script.
exlock_now || exit 1
# Remember! Lock file is removed when one of the scripts exits and it is
# the only script holding the lock or lock is not acquired at all.
flock
はおそらく最も簡単な(そして最も記憶に残る)バリアントだと思います。 dvds および cds を自動エンコードするためにcronジョブで使用します
# try to run a command, but fail immediately if it's already running
flock -n /var/lock/myjob.lock my_bash_command
つかいます -w
タイムアウトの場合、またはロックが解除されるまで待機するオプションを省略します。最後に、manページには複数のコマンドの素晴らしい例が示されています。
(
flock -n 9 || exit 1
# ... commands executed under lock ...
) 9>/var/lock/mylockfile
set -o noclobber
オプションを使用して、共通ファイルを上書きしようとします。if ! (set -o noclobber ; echo > /tmp/global.lock) ; then
exit 1 # the global.lock already exists
fi
# ... remainder of script ...
この例では、global.lock
ファイルですが、長すぎるとタイムアウトします。
function lockfile_waithold()
{
declare -ir time_beg=$(date '+%s')
declare -ir time_max=7140 # 7140 s = 1 hour 59 min.
# poll for lock file up to ${time_max}s
# put debugging info in lock file in case of issues ...
while ! \
(set -o noclobber ; \
echo -e "DATE:$(date)\nUSER:$(whoami)\nPID:$$" > /tmp/global.lock \
) 2>/dev/null
do
if [ $(($(date '+%s') - ${time_beg})) -gt ${time_max} ] ; then
echo "Error: waited too long for lock file /tmp/global.lock" 1>&2
return 1
fi
sleep 1
done
return 0
}
function lockfile_release()
{
rm -f /tmp/global.lock
}
if ! lockfile_waithold ; then
exit 1
fi
trap lockfile_release EXIT
# ... remainder of script ...
(これは、後に気付いた@Barry Kellyによる この投稿 に似ています。)
1行の堅牢なソリューションがあるかどうかはわかりません。そのため、最終的には独自のソリューションを導入することになります。
ロックファイルは不完全ですが、 'ps | grep | grep -v 'パイプライン。
そうは言っても、プロセス制御をスクリプトとは別にすることを検討することもできます-開始スクリプトを用意します。または、少なくとも別のファイルに保持されている関数に分解するため、呼び出し元スクリプトには次のようなものがあります。
. my_script_control.ksh
# Function exits if cannot start due to lockfile or prior running instance.
my_start_me_up lockfile_name;
trap "rm -f $lockfile_name; exit" 0 2 3 15
制御ロジックを必要とする各スクリプトで。 trap を使用すると、呼び出し元が終了したときにロックファイルが削除されるため、スクリプトの各終了ポイントでこれをコーディングする必要はありません。
別の制御スクリプトを使用することは、エッジケースの健全性チェックを可能にすることを意味します:古くなったログファイルを削除し、ロックファイルがスクリプトの現在実行中のインスタンスに正しく関連付けられていることを確認し、実行中のプロセスを強制終了するオプションを提供します。また、ps
出力でgrepを正常に使用できる可能性が高くなることも意味します。 ps-grepを使用すると、ロックファイルに実行中のプロセスが関連付けられていることを確認できます。おそらく、プロセスに関する情報を含めるために何らかの方法でロックファイルに名前を付けることができます:user、pidなど、ロックファイルを作成したプロセスがまだ存在するかどうかを判断するために、後のスクリプト呼び出しで使用できます。
[[ $(lsof -t $0| wc -l) > 1 ]] && echo "At least one of $0 is running"
currsh=$0
currpid=$$
runpid=$(lsof -t $currsh| paste -s -d " ")
if [[ $runpid == $currpid ]]
then
sleep 11111111111111111
else
echo -e "\nPID($runpid)($currpid) ::: At least one of \"$currsh\" is running !!!\n"
false
exit 1
fi
「lsof -t」は、「$ 0」という名前の現在実行中のスクリプトのすべてのPIDをリストします。
コマンド「lsof」には2つの利点があります。
私はこれをprocmailパッケージの依存関係で見つけました:
apt install liblockfile-bin
走る: dotlockfile -l file.lock
file.lockが作成されます。
ロック解除するには:dotlockfile -u file.lock
これを使用して、このパッケージファイル/コマンドをリストします:dpkg-query -L liblockfile-bin
Ubuntu/Debianディストリビューションにはstart-stop-daemon
あなたが説明するのと同じ目的のためのツール。 / etc/init.d/skeletonも参照して、開始/停止スクリプトの作成での使用方法を確認してください。
-ノア
ワンライン究極のソリューション:
[ "$(pgrep -fn $0)" -ne "$(pgrep -fo $0)" ] && echo "At least 2 copies of $0 are running"
chpst (runitの一部)を見ることもお勧めします。
chpst -L /tmp/your-lockfile.loc ./script.name.sh
私は同じ問題を抱えていて、ロックファイルを使用する template と、プロセスID番号を保持するpidファイルと、中止されたスクリプトが停止しないようにするkill -0 $(cat $pid_file)
チェックを思い付きました。次の実行。これにより、ロックファイルとpidファイルが存在する/ tmpにfoobar- $ USERIDフォルダーが作成されます。
これらのアクションをalertRunningPS
に保持している限り、スクリプトを呼び出して他のことを行うことができます。
#!/bin/bash
user_id_num=$(id -u)
pid_file="/tmp/foobar-$user_id_num/foobar-$user_id_num.pid"
lock_file="/tmp/foobar-$user_id_num/running.lock"
ps_id=$$
function alertRunningPS () {
local PID=$(cat "$pid_file" 2> /dev/null)
echo "Lockfile present. ps id file: $PID"
echo "Checking if process is actually running or something left over from crash..."
if kill -0 $PID 2> /dev/null; then
echo "Already running, exiting"
exit 1
else
echo "Not running, removing lock and continuing"
rm -f "$lock_file"
lockfile -r 0 "$lock_file"
fi
}
echo "Hello, checking some stuff before locking stuff"
# Lock further operations to one process
mkdir -p /tmp/foobar-$user_id_num
lockfile -r 0 "$lock_file" || alertRunningPS
# Do stuff here
echo -n $ps_id > "$pid_file"
echo "Running stuff in ONE ps"
sleep 30s
rm -f "$lock_file"
rm -f "$pid_file"
exit 0