Bash 스크립트의 하나의 인스턴스 만 실행되도록하는 가장 좋은 방법은 무엇입니까? [복제하다

StackOverflow https://stackoverflow.com/questions/1715137

문제

이 질문은 이미 여기에 답이 있습니다.

주어진 스크립트의 하나의 인스턴스 만 실행중인 가장 간단한/가장 좋은 방법은 무엇입니까?

지금은 내가하고있는 순간 :

ps -C script.name.sh > /dev/null 2>&1 || ./script.name.sh

그러나 몇 가지 문제가 있습니다.

  1. 스크립트 외부에서 확인을합니다
  2. 별도의 계정에서 동일한 스크립트를 실행할 수는 없습니다.
  3. -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 Page에서 사용 가능합니다. 요점). 그것을 하나의 사용자 당 하나의 사용자로 변환하는 것은 사소한 일입니다. 이를 사용하면 잠금 또는 동기화가 필요한 다른 시나리오에 대한 스크립트를 쉽게 작성할 수 있습니다.

편의를 위해 언급 된 보일러 플레이트가 있습니다.

#!/bin/bash
# SPDX-License-Identifier: MIT

## Copyright (C) 2009 Przemyslaw Pawelczyk <przemoc@gmail.com>
##
## 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 아마도 가장 쉬운 (그리고 가장 기억에 남는) 변형 일 것입니다. 나는 자동 인코 코드를 위해 cron 작업에서 그것을 사용합니다 DVD 그리고 CDS

# try to run a command, but fail immediately if it's already running
flock -n /var/lock/myjob.lock   my_bash_command

사용 -w 시간 초과 또는 잠금 장치가 해제 될 때까지 기다릴 옵션을 제거하십시오. 마지막으로 Man Page는 여러 명령에 대한 좋은 예를 보여줍니다.

   (
     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에 의해.)

한 줄의 강력한 솔루션이 있는지 잘 모르겠으므로 결국 자신만의 굴러 갈 수 있습니다.

자물쇠는 불완전하지만 '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

제어 로직이 필요한 각 스크립트에서. 그만큼 발신자가 종료 될 때 잠금 장치가 제거되도록하므로 스크립트의 각 종료 지점에서 코딩 할 필요가 없습니다.

별도의 컨트롤 스크립트를 사용한다는 것은 에지 케이스를 확인할 수 있음을 의미합니다. 오래된 로그 파일을 제거하고 Lockfile이 현재 실행중인 스크립트 인스턴스와 올바르게 연관되어 있는지 확인하고 실행중인 프로세스를 죽이는 옵션을 제공합니다. 그것은 또한 당신이 grep을 사용할 가능성이 더 높다는 것을 의미합니다. ps 성공적으로 출력. PS-GREP는 잠금 장치에 관련된 실행 프로세스가 있는지 확인하는 데 사용될 수 있습니다. 어쩌면 잠금 장치의 이름을 어떤 방식 으로든 이름을 지정하여 프로세스에 대한 정보를 포함시킬 수 있습니다 : 사용자, 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"명령은 두 가지 장점을 수행합니다.

  1. Vim은 ".file.swp"와 같은 매핑 파일을 편집하기 때문에 VIM과 같은 편집기가 편집하는 PID를 무시하십시오.
  2. 대부분의 "grep"파생 명령은이를 달성 할 수없는 현재 실행중인 쉘 스크립트에 의해 포장 된 PID를 무시합니다. "pstree -ph pidnum"명령을 사용하여 현재 프로세스 포킹 상태에 대한 세부 정보를 확인하십시오.

Procmail 패키지 종속성에서 이것을 찾았습니다.

apt install liblockfile-bin

실행하려면 :dotlockfile -l file.lock

file.lock이 생성됩니다.

잠금 해제 :dotlockfile -u file.lock

이 패키지 파일 / 명령을 나열하려면 이것을 사용하십시오. dpkg-query -L liblockfile-bin

우분투/데비안 배포판이 있습니다 start-stop-daemon 당신이 설명하는 것과 같은 목적을위한 도구. 또한보십시오 /etc/init.d/skeleton 시작/중지 스크립트를 작성하는 데 어떻게 사용되는지 확인합니다.

- 노아

또한 보는 것이 좋습니다 CHPST (runit의 일부) :

chpst -L /tmp/your-lockfile.loc ./script.name.sh

한 줄의 궁극적 인 솔루션 :

[ "$(pgrep -fn $0)" -ne "$(pgrep -fo $0)" ] && echo "At least 2 copies of $0 are running"

나는 같은 문제가 있었고 주형 이로 인해 프로세스 ID 번호를 보유하는 PID 파일 인 LockFile 및 kill -0 $(cat $pid_file) 중단 된 스크립트가 다음 실행을 멈추지 않도록 확인하십시오. 이로 인해 LockFile 및 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

"시스템 당 스크립트 사본"을 처리하는 매우 간단한 방법을 찾았습니다. 그래도 많은 계정에서 스크립트의 여러 사본을 실행할 수는 없습니다 (표준 Linux에서).

해결책:

스크립트가 시작될 때 나는 다음을 주었다.

pidof -s -o '%PPID' -x $( basename $0 ) > /dev/null 2>&1 && exit

보기에 PIDOF 다음과 같은 방식으로 잘 작동합니다.

  • 프로그램 이름과 같은 제한이 없습니다 ps -C ...
  • 내가 할 필요는 없습니다 grep -v grep (또는 비슷한 것)

그리고 그것은 Lockfiles에 의존하지 않습니다. 나에게 큰 승리는 큰 승리입니다. 릴레이는 당신이 실제로 복잡하지는 않지만 피할 수 있다면, 왜 그렇게하지 않습니까?

"사용자가 달리는 사용자 당 스크립트 사본 하나"로 확인하면 이것을 썼지 만 지나치게 만족하지는 않습니다.

(
    pidof -s -o '%PPID' -x $( basename $0 ) | tr ' ' '\n'
    ps xo pid= | tr -cd '[0-9\n]'
) | sort | uniq -d

그런 다음 출력이 비어있는 경우 출력을 확인합니다. 동일한 사용자의 스크립트 사본이 없습니다.

스크립트와 함께 :

ps -ef | grep $0 | grep $(whoami)

표준 비트는 다음과 같습니다. 잠금 장치를 청소하지 않고 어떻게 든 죽어가는 스크립트에서 복구 할 수 있습니다.

정상적으로 실행되면 프로세스 ID를 잠금 파일에 씁니다. 실행이 시작될 때 잠금 파일을 찾으면 잠금 파일에서 프로세스 ID를 읽고 해당 프로세스가 있는지 확인합니다. 프로세스가 존재하지 않으면 오래된 잠금 파일을 제거하고 수행합니다. 그리고 잠금 파일이 존재하고 프로세스가 여전히 실행중인 경우에만 종료됩니다. 그리고 그것은 종료 될 때 메시지를 씁니다.

# lock to ensure we don't get two copies of the same job
script_name="myscript.sh"
lock="/var/run/${script_name}.pid"
if [[ -e "${lock}" ]]; then
    pid=$(cat ${lock})
    if [[ -e /proc/${pid} ]]; then
        echo "${script_name}: Process ${pid} is still running, exiting."
        exit 1
    else
        # Clean up previous lock file
        rm -f ${lock}
   fi
fi
trap "rm -f ${lock}; exit $?" INT TERM EXIT
# write $$ (PID) to the lock file
echo "$$" > ${lock}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top