programing

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

bestprogram 2023. 4. 17. 22:34

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

Linux에서 Bash를 실행한다고 가정했을 때 특정 스크립트의 인스턴스를 하나만 실행할 수 있는 가장 단순하고 최선의 방법은 무엇입니까?

지금 하고 있는 일:

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

단, 몇 가지 문제가 있습니다.

  1. 체크가 스크립트 밖에 있다
  2. 다른 어카운트에서 같은 스크립트를 실행할 수 없습니다(가끔은 하고 싶은 경우도 있습니다.
  3. -C 프로세스 의 첫 합니다.

물론 PID 파일 처리도 직접 쓸 수 있지만 간단한 방법이 있을 것 같습니다.

권장 잠금 기능은 오랫동안 사용되어 왔으며 bash 스크립트에서 사용할 수 있습니다.는 단순한 것을 flock:util-linux[-ng] )의 개요lockfile:procmail의 트랩에 기억해 주세요(「spec 「스펙」 == 「스펙」 == 「스펙」EXIT ★★★★★★★★★★★★★★★★★」0특정 신호를 트래핑하는 것은 불필요한 것입니다).

2009년에 나는 잠글 수 있는 스크립트 보일러 플레이트를 발매했다(원래는 내 위키페이지에서 입수할 수 있었지만, 현재는 지스트로 입수할 수 있다).이를 사용자당 1인스턴스로 변환하는 것은 간단한 일입니다.또한 잠금 또는 동기화가 필요한 다른 시나리오의 스크립트를 쉽게 작성할 수도 있습니다.

여기 편의를 위해 언급한 보일러판이 있습니다.

#!/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.

간에 를 사용할 수 .lockfile if하여 메시지 후 종료합니다.잠금을 획득한 경우 계속 진행하거나 메시지를 표시한 후 종료합니다.

예를 들어 다음과 같습니다.

[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

생각에는flock아마도 가장 쉽고 기억에 남는 변종일 것입니다.cron 작업에서 DVD와 CD를 자동 인코딩하는 데 사용합니다.

# 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

bash bash 를 합니다.set -o noclobber옵션을 지정하고 공통 파일을 덮어씁니다.

프렌들리'은 '배시 프렌들리'를 할 때 합니다.flock사용할 수 없거나 해당되지 않습니다.

간단한 예

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 ...

이 기술은 장기간 실행되는 Ubuntu 16 호스트에서 안정적으로 작동했습니다.호스트는 동일한 시스템 전체의 "잠금" 파일을 사용하여 작업을 조정하는 bash 스크립트의 많은 인스턴스를 정기적으로 대기열에 넣습니다.

(이것은 나중에 알게 된 @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

제어 로직을 필요로 하는 각 스크립트에 있습니다.이 트랩을 사용하면 발신자가 종료할 때 잠금파일이 삭제되므로 스크립트의 각 종료 포인트에서 이를 코드화할 필요가 없습니다.

별도의 제어 스크립트를 사용하면 오래된 로그 파일을 삭제하고 잠금 파일이 현재 실행 중인 스크립트의 인스턴스와 올바르게 관련되어 있는지 확인하며 실행 중인 프로세스를 종료하는 옵션을 제공하는 등 Edge 케이스의 건전성을 확인할 수 있습니다.GREP에서 를 사용할 .ps이치노ps-blockp에 관련된 할 수 .프로세스에 대한 정보(user, pid 등)를 포함하기 위해 어떤 방법으로든 잠금 파일의 이름을 지정할 수 있습니다.이러한 이름을 나중에 스크립트 호출을 통해 잠금 파일을 작성한 프로세스가 아직 존재하는지 여부를 판단할 수 있습니다.

procmail 패키지의 의존관계에서 이것을 발견했습니다.

apt install liblockfile-bin

방법: " " " " "dotlockfile -l file.lock

file.lock이 생성됩니다.

해제 : " " " " " " "dotlockfile -u file.lock

하여 패키지 파일/명령어를 합니다: 명명명명 use use / this this this use use use use use 。 dpkg-query -L liblockfile-bin

첫 번째 테스트 예시

[[ $(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" 이라는 이름의 현재 실행 중인 스크립트의 모든 피드를 나열합니다.

명령어 "lsof"에는 두 가지 이점이 있습니다.

  1. vim은 ".file.swp"와 같은 매핑 파일을 편집하므로 vim과 같은 편집기에서 편집 중인 pids는 무시하십시오.
  2. 현재 실행 중인 셸 스크립트에서 분기된 pids를 무시합니다. 대부분의 "grep" 파생 명령에서는 이를 달성할 수 없습니다.현재 프로세스 포킹 상태에 대한 자세한 내용을 보려면 "pstree -pH pidnum" 명령을 사용합니다.

chpst(runit의 일부)도 참조할 것을 권장합니다.

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

Ubuntu/Debian Distros는 당신이 설명한 것과 같은 목적을 위한 도구를 가지고 있습니다.start/stop 스크립트의 기술 방법에 대해서는, /etc/init.d/skeleton 도 참조해 주세요.

--노아

한 줄의 궁극적인 솔루션:

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

저도 같은 문제가 있어서 lock file을 사용하는 템플릿, 프로세스 ID 번호를 저장하는 pid 파일, 그리고 pid 파일을 생각해 냈습니다.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

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

솔루션:

대본의 첫머리에서 저는 다음과 같이 말했습니다.

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

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

  • ps -C ...
  • 가 할 가 없다grep -v grep와 유사한 것 (「」)

또한 잠금 파일에 의존하지 않습니다. 잠금 파일에 대한 릴레이는 오래된 잠금 파일에 대한 처리를 추가해야 한다는 것을 의미하기 때문에 매우 유용합니다. 이 작업은 그다지 복잡하지 않지만 피할 수 있다면 왜 안 될까요?

'실행 중인 사용자 1인당 스크립트 1부' 체크에 대해서는 이렇게 썼지만 그다지 만족스럽지는 않습니다.

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

출력을 확인합니다(비어 있는 경우). 같은 사용자가 보낸 스크립트의 복사본이 없습니다.

이게 저희의 표준 비트입니다.잠금 파일을 정리하지 않고 스크립트가 소멸된 상태에서 복구할 수 있습니다.

정상적으로 동작하고 있는 경우는, 프로세스 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}

스크립트를 사용하여 다음을 수행합니다.

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

언급URL : https://stackoverflow.com/questions/1715137/what-is-the-best-way-to-ensure-only-one-instance-of-a-bash-script-is-running