한 번에 하나의 셸 스크립트 인스턴스 만 실행되도록하는 빠르고 더러운 방법


답변:


109

다음은 잠금 파일 을 사용 하고 PID를 에코 하는 구현입니다 . 이것은 pidfile을 제거하기 전에 프로세스가 종료 된 경우 보호 기능을합니다 .

LOCKFILE=/tmp/lock.txt
if [ -e ${LOCKFILE} ] && kill -0 `cat ${LOCKFILE}`; then
    echo "already running"
    exit
fi

# make sure the lockfile is removed when we exit and then claim it
trap "rm -f ${LOCKFILE}; exit" INT TERM EXIT
echo $$ > ${LOCKFILE}

# do stuff
sleep 1000

rm -f ${LOCKFILE}

여기서 트릭은 kill -0신호를 전달하지 않지만 주어진 PID를 가진 프로세스가 존재하는지 확인하는 것입니다. 또한 호출하는 trap수 있도록합니다 잠금 파일은 프로세스가 (제외 사망하는 경우도 제거됩니다 kill -9).


73
다른 답변에 대한 주석에서 이미 언급했듯이 이것은 치명적인 결함이 있습니다. 다른 스크립트가 검사와 에코 사이에서 시작되면 토스트입니다.
— Paul Tomblin

1
symlink 트릭은 깔끔하지만 lockfile의 소유자가 kill -9'd이거나 시스템이 충돌하는 경우 symlink를 읽어야하는 경쟁 조건이 여전히 존재하지만 소유자가 없어진 것을 확인한 후 삭제하십시오. 내 솔루션을 고수하고 있습니다.
— bmdhacks

9
원자 검사 및 작성은 쉘 (1) 또는 잠금 파일 (1)을 사용하여 쉘에서 사용 가능합니다. 다른 답변을 참조하십시오.
— dmckee --- 전 운영자 고양이

3
원자 검사를 수행하고 flock 또는 lockfile과 같은 유틸리티에 의존하지 않고 생성 할 수있는 이식 가능한 방법은 내 답변을 참조하십시오.
— lhunath 2009

2
이것은 원자 적이 지 않으므로 쓸모가 없습니다. 테스트 및 설정을위한 원자 메커니즘이 필요합니다.
— K Richard Pixley

214

사용은 flock(1)파일 기술자에 대한 독점적 인 범위의 잠금 A를 확인합니다. 이 방법으로 스크립트의 다른 부분을 동기화 할 수도 있습니다.

#!/bin/bash

(
  # Wait for lock on /var/lock/.myscript.exclusivelock (fd 200) for 10 seconds
  flock -x -w 10 200 || exit 1

  # Do stuff

) 200>/var/lock/.myscript.exclusivelock

코드 사이의이 보장하지만 (하고 )한 번에 하나 개의 프로세스 만이 실행되는 프로세스가 너무 오래 잠금을 대기하지 않습니다.

주의 사항 :이 특정 명령은의 일부입니다 util-linux. Linux 이외의 운영 체제를 실행하는 경우 운영 체제가 사용 가능하지 않을 수 있습니다.


11
200은 무엇입니까? 그것은 manul에서 "fd"라고 말하지만 그게 무슨 뜻인지 모르겠습니다.
— chovy

4
@chovy "파일 디스크립터", 열린 파일을 지정하는 정수 핸들.
— Alex B

6
다른 사람이 궁금한 경우 : 구문 ( command A ) command B은에 대한 서브 쉘을 호출합니다 command A. tldp.org/LDP/abs/html/subshells.html에 문서화되어 있습니다 . 나는 서브 쉘 및 명령 B의 호출의 타이밍에 대해 확실하지 아직 생각
— 박사 1 월 필립 Gehrcke

1
하위 쉘 내부의 코드는 더 비슷해야한다고 if flock -x -w 10 200; then ...Do stuff...; else echo "Failed to lock file" 1>&2; fi생각합니다. 시간 초과가 발생하면 (일부 다른 프로세스에 파일이 잠겨있는 경우)이 스크립트는 진행되지 않고 파일을 수정하지 않습니다. 아마도 ... 반복 인수는 '10 초가 걸렸지 만 잠금을 여전히 사용할 수 없으면 절대 사용할 수 없게 될 것입니다. '아마도 잠금을 유지하는 프로세스가 종료되지 않기 때문일 수 있습니다. 디버거에서?).
— Jonathan Leffler

1
리디렉션 된 파일은 잠금이 작동하는 장소 폴더 일 뿐이므로 의미있는 데이터가 없습니다. 는 exit안쪽 부분에서이다 ( ). 서브 프로세스가 종료되면 잠금을 보유하는 프로세스가 없으므로 잠금이 자동으로 해제됩니다.
— clacke

158

"파일 잠금"의 존재를 테스트하는 모든 접근 방식에는 결함이 있습니다.

왜? 파일이 존재하는지 확인하고 단일 원자 조치로 파일을 작성할 수있는 방법이 없기 때문입니다. 이것 때문에; 경쟁 조건이 것입니다 상호 배제 휴식에 당신의 시도를이.

대신을 사용해야 mkdir합니다. mkdir디렉토리가 없으면 디렉토리를 작성하고 존재하면 종료 코드를 설정합니다. 더 중요한 것은 단일 원자 동작 으로이 모든 것을 수행 하여이 시나리오에 완벽하게 만듭니다.

if ! mkdir /tmp/myscript.lock 2>/dev/null; then
    echo "Myscript is already running." >&2
    exit 1
fi

자세한 내용은 우수한 BashFAQ를 참조하십시오. http://mywiki.wooledge.org/BashFAQ/045

오래된 잠금 장치를 관리하려면 퓨저 (1) 가 유용합니다. 여기서 유일한 단점은 작업이 약 1 초가 걸리므로 즉각적이지 않다는 것입니다.

다음은 퓨저를 사용하여 문제를 해결하는 함수입니다.

#       mutex file
#
# Open a mutual exclusion lock on the file, unless another process already owns one.
#
# If the file is already locked by another process, the operation fails.
# This function defines a lock on a file as having a file descriptor open to the file.
# This function uses FD 9 to open a lock on the file.  To release the lock, close FD 9:
# exec 9>&-
#
mutex() {
    local file=$1 pid pids 

    exec 9>>"$file"
    { pids=$(fuser -f "$file"); } 2>&- 9>&- 
    for pid in $pids; do
        [[ $pid = $$ ]] && continue

        exec 9>&- 
        return 1 # Locked by a pid.
    done 
}

다음과 같이 스크립트에서 사용할 수 있습니다.

mutex /var/run/myscript.lock || { echo "Already running." >&2; exit 1; }

이식성에 신경 쓰지 않는다면 (이 솔루션은 거의 모든 UNIX 상자에서 작동해야합니다) Linux의 fuser (1) 은 몇 가지 추가 옵션을 제공하며 flock (1)도 있습니다.


1
if ! mkdirlockdir 내부에 저장된 PID가있는 프로세스 (실패한 시작시)가 실제로 실행 중이고 스탈린 보호를위한 스크립트 와 동일한 지 여부를 확인 하는 데 파트를 결합 할 수 있습니다 . 또한 재부팅 후 PID를 재사용하지 못하도록 보호 할 수 있으며조차 필요하지 않습니다 fuser.
— Tobias Kienzler

4
원 자성 조작으로 정의mkdir 되지 않았 으며 "부작용"이 파일 시스템의 구현 세부 사항 인 것은 확실합니다 . 그가 NFS가 원자적인 방식으로 그것을 구현하지 않는다고 말하면 그를 완전히 믿습니다. 비록 당신 이 NFS 공유가 될 것이라고 생각하지는 않지만 원자 적으로 구현하는 fs에 의해 제공 될 것 입니다. /tmpmkdir
— lhunath

5
그러나 일반 파일이 있는지 확인하고 그렇지 않은 경우 원자 적으로 파일을 만드는 방법 ln이 있습니다. 다른 파일에서 하드 링크를 만드는 데 사용 합니다. 보장 할 수없는 이상한 파일 시스템이있는 경우 나중에 새 파일의 inode를 검사하여 파일이 원본 파일과 동일한 지 확인할 수 있습니다.
— Juan Cespedes

4
가 이다 그것의 - '파일이 존재하는지 여부를 확인하고 하나의 원자 행동에 그것을 만들 수있는 방법이' open(... O_CREAT|O_EXCL). lockfile-create(in lockfile-progs) 또는 dotlockfile(in liblockfile-bin) 과 같이 적절한 사용자 프로그램이 필요합니다 . 또한 올바르게 청소 (예 trap EXIT:)하거나 오래된 잠금 장치 (예 :)를 테스트하십시오 --use-pid.
— Toby Speight

5
"파일 잠금"의 존재를 테스트하는 모든 접근 방식에는 결함이 있습니다. 왜? 파일이 존재하는지 확인하고 단일 원자 동작으로 파일을 작성할 수있는 방법이 없기 때문에 "-원자화하려면 커널 수준-커널 수준에서 flock (1) linux.die.net/man/1/flock 으로 수행됩니다.이 권한 은 맨 저작권 날짜부터 2006 년 이후부터 있었던 것으로 보입니다. 1) 개인적인 것은 아니지만 커널 개발자가 제공하는 커널 구현 도구를 사용하는 것이 옳다는 확신이 있습니다.
— 크레이그 cks 스

42

flock (2) 시스템 호출에는 flock (1)이라고하는 래퍼가 있습니다. 이렇게하면 정리 등을 걱정하지 않고 독점 잠금을 비교적 쉽게 얻을 수 있습니다 . 셸 스크립트에서이를 사용하는 방법 에 대한 예제 가 맨 페이지 에 있습니다.


3
flock()시스템 호출은 POSIX하지 않고 NFS 마운트에서 파일을 작동하지 않습니다.
— maxschlepzig

17
flock -x -n %lock file% -c "%command%"하나의 인스턴스 만 실행되도록하기 위해 사용하는 Cron 작업에서 실행
— Ryall

Aww, 상상력이없는 flock (1) 대신 flock (U)와 같은 것을 사용해야했습니다. ... 그것에 익숙합니다. . 한두 시간 전에 들었던 것 같습니다.
— Kent Kruckeberg

flock (2) 문서는 파일 만 사용하도록 지정하지만 flock (1) 문서는 파일 또는 디렉토리 사용을 지정합니다. flock (1) 문서는 작성 중 차이를 표시하는 방법에 대해 명시 적이 지 않지만 최종 "/"를 추가하여 수행한다고 가정합니다. 어쨌든 flock (1)이 디렉토리를 처리 할 수 ​​있지만 flock (2)는 처리 할 수없는 경우 flock (1)은 flock (2)에서만 구현되지 않습니다.
— 크레이그 cks 스

27

무리와 같은 원자 연산이 필요합니다. 그렇지 않으면 결국 실패합니다.

그러나 무리를 사용할 수 없으면 어떻게해야합니까? mkdir이 있습니다. 그것은 원자 작업입니다. 하나의 프로세스 만 mkdir을 성공적으로 수행하고 다른 프로세스는 모두 실패합니다.

코드는 다음과 같습니다.

if mkdir /var/lock/.myscript.exclusivelock
then
  # do stuff
  :
  rmdir /var/lock/.myscript.exclusivelock
fi

스크립트가 다시 실행되지 않는 충돌이 발생하면 오래된 잠금을 처리해야합니다.


1
"./a.sh & ./a.sh & ./a.sh & ./a.sh & ./a.sh & ./a.sh & ./a.sh와 같이 동시에 몇 번 실행하십시오. & ") 스크립트가 몇 번 새어 나옵니다.
— Nippysaurus

7
@Nippysaurus :이 잠금 방법은 누출되지 않습니다. 당신이 본 것은 모든 복사본이 시작되기 전에 초기 스크립트가 종료되었으므로 다른 스크립트가 잠금을 올바르게 얻을 수있었습니다. 이 오탐을 피하려면 sleep 10이전을 추가하고 rmdir다시 캐스케이드를 시도하십시오. "누설"이 없습니다.
— 아토스 경

다른 소스는 mkdir이 NFS와 같은 일부 파일 시스템에서 원 자성이 아니라고 주장합니다. 그리고 btw는 NFS 동시 재귀 mkdir이 때로는 jenkins 매트릭스 작업에서 오류를 발생시키는 경우를 보았습니다. 그래서 나는 그것이 사실이라고 확신합니다. 그러나 mkdir은 덜 까다로운 사용 사례 IMO에 매우 좋습니다.
— akostadinov

Bash의 noclobber 옵션을 일반 파일과 함께 사용할 수 있습니다.
— Palec

26

잠금을 안정적으로 만들려면 원 자성 작업이 필요합니다. 위의 제안 중 많은 부분이 원자가 아닙니다. 제안 된 lockfile (1) 유틸리티는 언급 된 맨 페이지에서 "NFS- 내성"이라는 유망한 것으로 보입니다. OS가 lockfile (1)을 지원하지 않고 솔루션이 NFS에서 작동해야하는 경우 옵션이 많지 않습니다 ...

NFSv2에는 두 가지 원자 연산이 있습니다.

  • 심볼릭 링크
  • 이름을 바꾸다

NFSv3을 사용하면 create 호출도 원 자성입니다.

디렉토리 작업은 NFSv2 및 NFSv3에서 원자 적이 지 않습니다 (Brent Callaghan의 ISBN 'NFS Illustrated'(ISBN 0-201-32570-5, Brent는 Sun의 NFS 베테랑) 참조).

이것을 알면 파일과 디렉토리 (PHP가 아닌 셸)에 대한 스핀 잠금을 구현할 수 있습니다.

현재 디렉토리 잠금 :

while ! ln -s . lock; do :; done

파일을 잠그십시오.

while ! ln -s ${f} ${f}.lock; do :; done

현재 디렉토리 잠금 해제 (가정, 실행중인 프로세스가 실제로 잠금을 획득 함) :

mv lock deleteme && rm deleteme

파일 잠금 해제 (가정, 실행중인 프로세스가 실제로 잠금을 획득 함) :

mv ${f}.lock ${f}.deleteme && rm ${f}.deleteme

제거도 원자가 아니므로 먼저 원자 이름을 변경 한 다음 제거하십시오.

심볼릭 링크 및 이름 바꾸기 호출의 경우 두 파일 이름이 모두 동일한 파일 시스템에 있어야합니다. 내 제안 : 간단한 파일 이름 (경로 없음) 만 사용하고 파일을 넣고 동일한 디렉토리에 고정하십시오.


mkdir이 NFS보다 원자 적이 지 않다는 문장을 지원하는 NFS Illustrated 페이지는 무엇입니까?
— maxschlepzig

이 기술에 대한 생각. 쉘 뮤텍스 구현은 나의 새로운 쉘 lib : github.com/Offirmo/offirmo-shell-lib 에서 가능하다. "mutex"를 보라. 이 사용 lockfile가능한 경우, 또는 대체이로 symlink하지 않을 경우 방법.
— Offirmo

좋은. 불행히도이 방법은 오래된 잠금을 자동으로 삭제하는 방법을 제공하지 않습니다.
— Richard Hansen

두 단계 P1, P2가 경주하는 경우가 아니라 두 단계 잠금 해제 ( mv, rm)를 사용해야 합니까? 예를 들어, P1은로 잠금 해제를 시작한 다음 P2 잠금, P2 잠금 해제 ( 및 모두 )를 시작하고 마지막으로 P1이 시도 하고 실패합니다. rm -frmmvmvrmrm
— 매트 월리스

1
@MattWallis 마지막 문제 $$는 ${f}.deleteme파일 이름 에 포함시켜 쉽게 완화 할 수 있습니다 .
— Stefan Majewsky

23

또 다른 옵션은을 noclobber실행 하여 쉘 옵션을 사용 하는 것 set -C입니다. 그때> 파일이 이미 존재하는 경우 실패합니다.

간단히 :

set -C
lockfile="/tmp/locktest.lock"
if echo "$$" > "$lockfile"; then
    echo "Successfully acquired lock"
    # do work
    rm "$lockfile"    # XXX or via trap - see below
else
    echo "Cannot acquire lock - already locked by $(cat "$lockfile")"
fi

쉘이 다음을 호출하게합니다.

open(pathname, O_CREAT|O_EXCL)

원자 적으로 파일을 작성하거나 파일이 이미 존재하는 경우 실패합니다.


BashFAQ 045 에 대한 의견에 따르면 이것은 실패 할 수 ksh88있지만 모든 쉘에서 작동합니다.

$ strace -e trace=creat,open -f /bin/bash /home/mikel/bin/testopen 2>&1 | grep -F testopen.lock
open("/tmp/testopen.lock", O_WRONLY|O_CREAT|O_EXCL|O_LARGEFILE, 0666) = 3

$ strace -e trace=creat,open -f /bin/zsh /home/mikel/bin/testopen 2>&1 | grep -F testopen.lock
open("/tmp/testopen.lock", O_WRONLY|O_CREAT|O_EXCL|O_NOCTTY|O_LARGEFILE, 0666) = 3

$ strace -e trace=creat,open -f /bin/pdksh /home/mikel/bin/testopen 2>&1 | grep -F testopen.lock
open("/tmp/testopen.lock", O_WRONLY|O_CREAT|O_EXCL|O_TRUNC|O_LARGEFILE, 0666) = 3

$ strace -e trace=creat,open -f /bin/dash /home/mikel/bin/testopen 2>&1 | grep -F testopen.lock
open("/tmp/testopen.lock", O_WRONLY|O_CREAT|O_EXCL|O_LARGEFILE, 0666) = 3

플래그 를 pdksh추가하는 O_TRUNC것이 흥미롭지 만 분명히 중복됩니다.
빈 파일을 만들거나 아무것도하지 않습니다.


rm부정 행위 를 처리 할 방법에 따라 방법이 달라집니다.

정리 종료시 삭제

부정확 한 종료가 발생한 문제가 해결되고 잠금 파일이 수동으로 제거 될 때까지 새 실행이 실패합니다.

# acquire lock
# do work (code here may call exit, etc.)
rm "$lockfile"

모든 출구에서 삭제

스크립트가 아직 실행되고 있지 않으면 새로운 실행이 성공합니다.

trap 'rm "$lockfile"' EXIT

매우 참신한 접근 방법 ... 이것은 잠금 디렉토리가 아닌 잠금 파일을 사용하여 원 자성을 달성하는 한 가지 방법으로 보입니다.
— 매트 콜드웰

좋은 접근법. :-) EXIT 트랩에서는 잠금 파일을 정리할 수있는 프로세스를 제한해야합니다. 예를 들면 다음과 같습니다. trap 'if [[$ (cat "$ lockfile") == "$$"]]; 그런 다음 rm "$ lockfile"; fi 'EXIT
— Kevin Seifert

1
잠금 파일은 NFS를 통해 원자 적이 지 않습니다. 이것이 사람들이 잠금 디렉토리를 사용하는 이유입니다.
— K Richard Pixley

20

GNU Parallel로 호출 할 때 뮤텍스로 작동하므로 이것을 사용할 수 있습니다 sem. 따라서 구체적인 용어로 다음을 사용할 수 있습니다.

sem --id SCRIPTSINGLETON yourScript

시간 초과를 원하면 다음을 사용하십시오.

sem --id SCRIPTSINGLETON --semaphoretimeout -10 yourScript

시간 종료 <0은 시간 종료 내에 세마포어가 해제되지 않은 경우 스크립트를 실행하지 않고 종료하는 것을 의미하며, 시간 종료> 0은 스크립트를 실행하는 것을 의미합니다.

이름을 (로 --id) 지정해야하며 그렇지 않으면 제어 터미널이 기본값이됩니다.

GNU Parallel 대부분의 Linux / OSX / Unix 플랫폼에서 설치가 매우 간단합니다. Perl 스크립트 일뿐입니다.


너무 나쁜 사람들은 쓸모없는 답변을 거절하는 것을 꺼려합니다. 이는 새로운 더미에 정크에 묻 힙니다.
— Dmitry Grigoryev

4
우리는 단지 많은 투표를 필요로합니다. 이것은 깔끔하고 거의 알려지지 않은 대답입니다. (절대적으로 OP가 되기는했지만 빠르고 더러움을 원했지만 이것은 빠르고 깨끗합니다!) sem관련 질문 unix.stackexchange.com/a/322200/199525 에서 자세히 알아보십시오 .
— 부분적으로 흐린

16

쉘 스크립트의 경우, 나는 mkdir오버 와 함께 경향이flock 경우 잠금을 더 이식 가능하게 만들면서 났습니다.

어느 쪽이든 set -e 것만으로는 충분하지 않습니다. 명령이 실패하면 스크립트를 종료합니다. 자물쇠는 여전히 남아 있습니다.

적절한 잠금 정리를 위해서는 트랩을이 의사 코드와 같이 설정해야합니다 (리프팅, 단순화 및 테스트되지 않았지만 활발하게 사용되는 스크립트에서).

#=======================================================================
# Predefined Global Variables
#=======================================================================

TMPDIR=/tmp/myapp
[[ ! -d $TMP_DIR ]] \
    && mkdir -p $TMP_DIR \
    && chmod 700 $TMPDIR

LOCK_DIR=$TMP_DIR/lock

#=======================================================================
# Functions
#=======================================================================

function mklock {
    __lockdir="$LOCK_DIR/$(date +%s.%N).$$" # Private Global. Use Epoch.Nano.PID

    # If it can create $LOCK_DIR then no other instance is running
    if $(mkdir $LOCK_DIR)
    then
        mkdir $__lockdir  # create this instance's specific lock in queue
        LOCK_EXISTS=true  # Global
    else
        echo "FATAL: Lock already exists. Another copy is running or manually lock clean up required."
        exit 1001  # Or work out some sleep_while_execution_lock elsewhere
    fi
}

function rmlock {
    [[ ! -d $__lockdir ]] \
        && echo "WARNING: Lock is missing. $__lockdir does not exist" \
        || rmdir $__lockdir
}

#-----------------------------------------------------------------------
# Private Signal Traps Functions {{{2
#
# DANGER: SIGKILL cannot be trapped. So, try not to `kill -9 PID` or 
#         there will be *NO CLEAN UP*. You'll have to manually remove 
#         any locks in place.
#-----------------------------------------------------------------------
function __sig_exit {

    # Place your clean up logic here 

    # Remove the LOCK
    [[ -n $LOCK_EXISTS ]] && rmlock
}

function __sig_int {
    echo "WARNING: SIGINT caught"    
    exit 1002
}

function __sig_quit {
    echo "SIGQUIT caught"
    exit 1003
}

function __sig_term {
    echo "WARNING: SIGTERM caught"    
    exit 1015
}

#=======================================================================
# Main
#=======================================================================

# Set TRAPs
trap __sig_exit EXIT    # SIGEXIT
trap __sig_int INT      # SIGINT
trap __sig_quit QUIT    # SIGQUIT
trap __sig_term TERM    # SIGTERM

mklock

# CODE

exit # No need for cleanup code here being in the __sig_exit trap function

어떻게 될까요? 모든 트랩은 출구를 생성하므로 __sig_exit잠금을 정리 하는 기능 이 항상 발생합니다 (SIGKILL 제외).

참고 : 이탈 값은 낮은 값이 아닙니다. 왜? 다양한 배치 처리 시스템은 0에서 31까지의 숫자를 기대하거나 갖습니다.이를 다른 것으로 설정하면 스크립트 및 배치 스트림이 이전 배치 작업 또는 스크립트에 따라 반응하도록 할 수 있습니다.


2
스크립트가 너무 장황하여 생각보다 훨씬 짧을 수도 있지만 전반적으로 그렇습니다. 트랩을 올바르게 설정해야합니다. 또한 SIGHUP을 추가하겠습니다.
— mojuba

$ LOCK_DIR을 확인하는 반면 $ __ lockdir을 제거하는 것을 제외하고는 잘 작동합니다. 어쩌면 잠금을 제거 할 때 rm -r $ LOCK_DIR을 수행 할 것을 제안해야합니까?
— bevada

제안 해 주셔서 감사합니다. 위의 코드는 해제되고 의사 코드 방식으로 배치되었으므로 사람들의 사용에 따라 조정해야합니다. 그러나 rmdir은 디렉토리 가 비어있는 경우 only_ 디렉토리를 안전하게 제거하므로 내 경우에는 의도적으로 rmdir을 사용했습니다 . 사람들이 PID 파일 등과 같은 리소스를 리소스에 배치하는 경우 잠금 정리를보다 적극적으로 변경 rm -r $LOCK_DIR하거나 필요에 따라 강제로 수정해야합니다 (상대 스크래치 파일을 보유하는 것과 같은 특수한 경우에도 마찬가지입니다). 건배.
— Mark Stinson

테스트 했습니까 exit 1002?
— Gilles Quenot 2016 년

13

정말 빠르고 정말 더럽습니까? 스크립트 상단에있는이 하나의 라이너는 다음과 같이 작동합니다.

[[ $(pgrep -c "`basename \"$0\"`") -gt 1 ]] && exit

물론 스크립트 이름이 고유한지 확인하십시오. :)


테스트하기 위해 이것을 어떻게 시뮬레이션합니까? 스크립트를 한 줄에 두 번 시작하고 이미 실행 중일 경우 경고를받을 수있는 방법이 있습니까?
— rubo77

2
이것은 전혀 작동하지 않습니다! 왜 확인 -gt 2합니까? grep이 ps의 결과에서 항상 자신을 찾지는 않습니다!
— rubo77

pgrepPOSIX에 없습니다. 이 작업을 이식 가능하게하려면 POSIX가 필요 ps하고 출력을 처리하십시오.
— Palec

OSX -c가 존재하지 않으면를 사용해야 | wc -l합니다. 숫자 비교 정보 : -gt 1첫 번째 인스턴스 자체를 확인한 후 확인됩니다.
— Benjamin Peter

6

원자 디렉토리 잠금과 PID를 통한 오래된 잠금 확인을 결합하고 오래된 경우 다시 시작하는 방법이 있습니다. 또한 이것은 bashism에 의존하지 않습니다.

#!/bin/dash

SCRIPTNAME=$(basename $0)
LOCKDIR="/var/lock/${SCRIPTNAME}"
PIDFILE="${LOCKDIR}/pid"

if ! mkdir $LOCKDIR 2>/dev/null
then
    # lock failed, but check for stale one by checking if the PID is really existing
    PID=$(cat $PIDFILE)
    if ! kill -0 $PID 2>/dev/null
    then
       echo "Removing stale lock of nonexistent PID ${PID}" >&2
       rm -rf $LOCKDIR
       echo "Restarting myself (${SCRIPTNAME})" >&2
       exec "$0" "$@"
    fi
    echo "$SCRIPTNAME is already running, bailing out" >&2
    exit 1
else
    # lock successfully acquired, save PID
    echo $$ > $PIDFILE
fi

trap "rm -rf ${LOCKDIR}" QUIT INT TERM EXIT


echo hello

sleep 30s

echo bye

5

알려진 위치에 잠금 파일을 작성하고 스크립트 시작시 존재 여부를 확인 하시겠습니까? 누군가 PID를 파일에 넣으면 스크립트 실행을 방해하는 잘못된 인스턴스를 추적하려는 경우 도움이 될 수 있습니다.


5

이 예제는 man flock에 설명되어 있지만 버그와 종료 코드를 관리해야하기 때문에 약간의 노력이 필요합니다.

   #!/bin/bash
   #set -e this is useful only for very stupid scripts because script fails when anything command exits with status more than 0 !! without possibility for capture exit codes. not all commands exits >0 are failed.

( #start subprocess
  # Wait for lock on /var/lock/.myscript.exclusivelock (fd 200) for 10 seconds
  flock -x -w 10 200
  if [ "$?" != "0" ]; then echo Cannot lock!; exit 1; fi
  echo $$>>/var/lock/.myscript.exclusivelock #for backward lockdir compatibility, notice this command is executed AFTER command bottom  ) 200>/var/lock/.myscript.exclusivelock.
  # Do stuff
  # you can properly manage exit codes with multiple command and process algorithm.
  # I suggest throw this all to external procedure than can properly handle exit X commands

) 200>/var/lock/.myscript.exclusivelock   #exit subprocess

FLOCKEXIT=$?  #save exitcode status
    #do some finish commands

exit $FLOCKEXIT   #return properly exitcode, may be usefull inside external scripts

다른 방법을 사용하고 과거에 사용한 프로세스를 나열 할 수 있습니다. 그러나 이것은 위의 방법보다 더 복잡합니다. 기생충 nad 제거를 위해 ps, 프로세스 이름, 추가 필터 grep -v grep로 프로세스를 나열해야합니다. 숫자와 비교하십시오. 복잡하고 불확실하다


1
ln -s를 사용할 수 있습니다. mkdir과 ​​같은 파일 또는 symlink가없는 경우에만 symlink를 작성할 수 있습니다. 과거에는 많은 시스템 프로세스가 예를 들어 init 또는 inetd와 같은 심볼릭 링크를 사용했습니다. synlink는 프로세스 ID를 유지하지만 실제로 아무것도 가리 키지 않습니다. 몇 년 동안이 행동이 변경되었습니다. 프로세스는 플록과 세마포어를 사용합니다.
— Znik

5

게시 된 기존 답변은 CLI 유틸리티를 사용 flock하거나 잠금 파일을 제대로 보호하지 않습니다. flock 유틸리티는 모든 비 Linux 시스템 (예 : FreeBSD)에서 사용할 수 없으며 NFS에서 제대로 작동하지 않습니다.

시스템 관리 및 시스템 개발의 제 초기에, 나는 잠금 파일을 만드는 안전하고 상대적으로 휴대용 방법을 사용하여 임시 파일을 생성하는 것을 들었다 mkemp(3)거나 mkemp(1), 임시 파일 (예 : PID)에 식별 정보를 기입 한 후 하드 링크 잠금 파일에 임시 파일. 연결에 성공하면 잠금을 획득 한 것입니다.

쉘 스크립트에서 잠금을 사용하는 경우 일반적으로 obtain_lock()공유 프로파일에 함수를 배치 한 다음 스크립트에서 소스를 제공합니다. 아래는 내 잠금 기능의 예입니다.

obtain_lock()
{
  LOCK="${1}"
  LOCKDIR="$(dirname "${LOCK}")"
  LOCKFILE="$(basename "${LOCK}")"

  # create temp lock file
  TMPLOCK=$(mktemp -p "${LOCKDIR}" "${LOCKFILE}XXXXXX" 2> /dev/null)
  if test "x${TMPLOCK}" == "x";then
     echo "unable to create temporary file with mktemp" 1>&2
     return 1
  fi
  echo "$$" > "${TMPLOCK}"

  # attempt to obtain lock file
  ln "${TMPLOCK}" "${LOCK}" 2> /dev/null
  if test $? -ne 0;then
     rm -f "${TMPLOCK}"
     echo "unable to obtain lockfile" 1>&2
     if test -f "${LOCK}";then
        echo "current lock information held by: $(cat "${LOCK}")" 1>&2
     fi
     return 2
  fi
  rm -f "${TMPLOCK}"

  return 0;
};

다음은 잠금 기능을 사용하는 방법의 예입니다.

#!/bin/sh

. /path/to/locking/profile.sh
PROG_LOCKFILE="/tmp/myprog.lock"

clean_up()
{
  rm -f "${PROG_LOCKFILE}"
}

obtain_lock "${PROG_LOCKFILE}"
if test $? -ne 0;then
   exit 1
fi
trap clean_up SIGHUP SIGINT SIGTERM

# bulk of script

clean_up
exit 0
# end of script

전화해야합니다 clean_up스크립트의 종료점 .

나는 리눅스와 FreeBSD 환경에서 위의 것을 사용했다.


4

데비안 머신을 대상으로 할 때 lockfile-progs패키지가 좋은 해결책이라고 생각합니다. 도구 procmail도 함께 제공됩니다 lockfile. 그러나 때때로 나는 이것들 중 어느 것에도 붙어 있지 않습니다.

다음 mkdir은 원 자성 및 PID 파일을 사용하여 오래된 잠금을 감지하는 솔루션입니다 . 이 코드는 현재 Cygwin 설정에서 제작 중이며 제대로 작동합니다.

그것을 사용하려면 exclusive_lock_require무언가에 독점적으로 액세스해야 할 때 전화하십시오 . 선택적 잠금 이름 매개 변수를 사용하면 다른 스크립트간에 잠금을 공유 할 수 있습니다. 더 복잡한 것이 필요한 경우 에는 두 가지 하위 수준 함수 ( exclusive_lock_try및 exclusive_lock_retry)가 있습니다.

function exclusive_lock_try() # [lockname]
{

    local LOCK_NAME="${1:-`basename $0`}"

    LOCK_DIR="/tmp/.${LOCK_NAME}.lock"
    local LOCK_PID_FILE="${LOCK_DIR}/${LOCK_NAME}.pid"

    if [ -e "$LOCK_DIR" ]
    then
        local LOCK_PID="`cat "$LOCK_PID_FILE" 2> /dev/null`"
        if [ ! -z "$LOCK_PID" ] && kill -0 "$LOCK_PID" 2> /dev/null
        then
            # locked by non-dead process
            echo "\"$LOCK_NAME\" lock currently held by PID $LOCK_PID"
            return 1
        else
            # orphaned lock, take it over
            ( echo $$ > "$LOCK_PID_FILE" ) 2> /dev/null && local LOCK_PID="$$"
        fi
    fi
    if [ "`trap -p EXIT`" != "" ]
    then
        # already have an EXIT trap
        echo "Cannot get lock, already have an EXIT trap"
        return 1
    fi
    if [ "$LOCK_PID" != "$$" ] &&
        ! ( umask 077 && mkdir "$LOCK_DIR" && umask 177 && echo $$ > "$LOCK_PID_FILE" ) 2> /dev/null
    then
        local LOCK_PID="`cat "$LOCK_PID_FILE" 2> /dev/null`"
        # unable to acquire lock, new process got in first
        echo "\"$LOCK_NAME\" lock currently held by PID $LOCK_PID"
        return 1
    fi
    trap "/bin/rm -rf \"$LOCK_DIR\"; exit;" EXIT

    return 0 # got lock

}

function exclusive_lock_retry() # [lockname] [retries] [delay]
{

    local LOCK_NAME="$1"
    local MAX_TRIES="${2:-5}"
    local DELAY="${3:-2}"

    local TRIES=0
    local LOCK_RETVAL

    while [ "$TRIES" -lt "$MAX_TRIES" ]
    do

        if [ "$TRIES" -gt 0 ]
        then
            sleep "$DELAY"
        fi
        local TRIES=$(( $TRIES + 1 ))

        if [ "$TRIES" -lt "$MAX_TRIES" ]
        then
            exclusive_lock_try "$LOCK_NAME" > /dev/null
        else
            exclusive_lock_try "$LOCK_NAME"
        fi
        LOCK_RETVAL="${PIPESTATUS[0]}"

        if [ "$LOCK_RETVAL" -eq 0 ]
        then
            return 0
        fi

    done

    return "$LOCK_RETVAL"

}

function exclusive_lock_require() # [lockname] [retries] [delay]
{
    if ! exclusive_lock_retry "$@"
    then
        exit 1
    fi
}

고마워, cygwin에서 직접 시도해 보았고 간단한 테스트를 통과했습니다.
— ndemou 2016 년

4

이 스레드의 다른 곳에서 이미 설명한 무리의 제한이 문제가되지 않으면 다음과 같이 작동합니다.

#!/bin/bash

{
    # exit if we are unable to obtain a lock; this would happen if 
    # the script is already running elsewhere
    # note: -x (exclusive) is the default
    flock -n 100 || exit

    # put commands to run here
    sleep 100
} 100>/tmp/myjob.lock 

3
-x (쓰기 잠금)가 이미 기본적으로 설정되어 있다고 지적했습니다.
— Keldon Alleyne

그리고 -n것입니다 exit 1그것은 잠금을 얻을 수없는 경우 즉시
— Anentropic

@KeldonAlleyne에게 감사합니다. 기본값 인 "-x"를 제거하도록 코드를 업데이트했습니다.
— presto8

3

일부 유닉스는 lockfile 이미 언급 한 것과 매우 유사합니다 flock.

맨 페이지에서 :

lockfile은 하나 이상의 세마포어 파일을 작성하는 데 사용될 수 있습니다. lock-file이 지정된 모든 파일을 지정된 순서대로 작성할 수 없으면 휴면 시간 (기본값 8 초) 동안 대기 한 후 성공하지 못한 마지막 파일을 재 시도합니다. 실패가 리턴 될 때까지 재시도 횟수를 지정할 수 있습니다. 재시도 횟수가 -1 (기본값, -r-1)이면 잠금 파일이 영구적으로 재 시도됩니다.


우리는 어떻게 lockfile유틸리티를 얻 습니까?
— Offirmo

lockfile와 함께 배포됩니다 procmail. 또한 패키지 dotlockfile와 함께 제공 되는 대안 이 liblockfile있습니다. 둘 다 NFS에서 안정적으로 작동한다고 주장합니다.
— Mr. Deathless

3

실제로 bmdhacks의 대답은 거의 좋지만 잠금 파일을 처음 확인한 후 기록하기 전에 두 번째 스크립트가 실행될 가능성이 약간 있습니다. 따라서 둘 다 잠금 파일을 작성하고 둘 다 실행됩니다. 확실하게 작동시키는 방법은 다음과 같습니다.

lockfile=/var/lock/myscript.lock

if ( set -o noclobber; echo "$$" > "$lockfile") 2> /dev/null ; then
  trap 'rm -f "$lockfile"; exit $?' INT TERM EXIT
else
  # or you can decide to skip the "else" part if you want
  echo "Another instance is already running!"
fi

이 noclobber옵션은 파일이 이미 존재하는 경우 경로 재 지정 명령이 실패하는지 확인합니다. 따라서 redirect 명령은 실제로 원자 적입니다. 하나의 명령으로 파일을 작성하고 확인하십시오. 파일 끝에서 잠금 파일을 제거 할 필요는 없습니다. 트랩에 의해 제거됩니다. 나는 이것이 나중에 읽을 사람들에게 도움이되기를 바랍니다.

추신 : 나는 Mikel이 이미 질문에 올바르게 대답 한 것을 보지 못했지만, 예를 들어 Ctrl-C로 스크립트를 중지 한 후 잠금 파일이 남을 가능성을 줄이기 위해 trap 명령을 포함하지 않았습니다. 이것이 완벽한 솔루션입니다


3

잠금 파일, lockdir, 특수 잠금 프로그램을 제거하고 pidof모든 Linux 설치에서 찾을 수 없기 때문에 제거하고 싶었습니다 . 또한 가능한 가장 간단한 코드 (또는 가능한 한 적은 수의 행)를 원했습니다. if한 줄로 가장 간단한 진술 :

if [[ $(ps axf | awk -v pid=$$ '$1!=pid && $6~/'$(basename $0)'/{print $1}') ]]; then echo "Already running"; exit; fi

1
이것은 내 컴퓨터의 'ps'출력에 민감합니다 (우분투 14.04, procps-ng 버전 3.3.9의 / bin / ps) 'ps axf'명령은 필드 번호를 방해하는 ASCII 트리 문자를 인쇄합니다. 이것은 나를 위해 일했다 : /bin/ps -a --format pid,cmd | awk -v pid=$$ '/'$(basename $0)'/ { if ($1!=pid) print $1; }'
— qneill

2

오래된 잠금 파일을 처리하는 간단한 방법을 사용합니다.

pid를 저장하는 위의 솔루션 중 일부는 pid가 랩핑 될 수 있다는 사실을 무시하십시오. 따라서 저장된 pid로 유효한 프로세스가 있는지 확인하는 것만으로는 충분하지 않습니다. 특히 장시간 실행되는 스크립트의 경우 충분합니다.

noclobber를 사용하여 한 번에 하나의 스크립트 만 열고 잠금 파일에 쓸 수 있도록합니다. 또한 잠금 파일에서 프로세스를 고유하게 식별하기에 충분한 정보를 저장합니다. 나는 pid, ppid, lstart 할 프로세스를 고유하게 식별하기 위해 데이터 세트를 정의합니다.

새 스크립트가 시작될 때 잠금 파일을 작성하지 못하면 잠금 파일을 작성한 프로세스가 여전히 주위에 있는지 확인합니다. 그렇지 않다면, 우리는 원래의 프로세스가 부적절하게 사망하여 오래된 잠금 파일을 남겼다고 가정합니다. 그런 다음 새 스크립트는 잠금 파일의 소유권을 가져 오며 모두 다시 세상에 있습니다.

여러 플랫폼에서 여러 쉘로 작업해야합니다. 빠르고 휴대가 간편합니다.

#!/usr/bin/env sh
# Author: rouble

LOCKFILE=/var/tmp/lockfile #customize this line

trap release INT TERM EXIT

# Creates a lockfile. Sets global variable $ACQUIRED to true on success.
# 
# Returns 0 if it is successfully able to create lockfile.
acquire () {
    set -C #Shell noclobber option. If file exists, > will fail.
    UUID=`ps -eo pid,ppid,lstart $$ | tail -1`
    if (echo "$UUID" > "$LOCKFILE") 2>/dev/null; then
        ACQUIRED="TRUE"
        return 0
    else
        if [ -e $LOCKFILE ]; then 
            # We may be dealing with a stale lock file.
            # Bring out the magnifying glass. 
            CURRENT_UUID_FROM_LOCKFILE=`cat $LOCKFILE`
            CURRENT_PID_FROM_LOCKFILE=`cat $LOCKFILE | cut -f 1 -d " "`
            CURRENT_UUID_FROM_PS=`ps -eo pid,ppid,lstart $CURRENT_PID_FROM_LOCKFILE | tail -1`
            if [ "$CURRENT_UUID_FROM_LOCKFILE" == "$CURRENT_UUID_FROM_PS" ]; then 
                echo "Script already running with following identification: $CURRENT_UUID_FROM_LOCKFILE" >&2
                return 1
            else
                # The process that created this lock file died an ungraceful death. 
                # Take ownership of the lock file.
                echo "The process $CURRENT_UUID_FROM_LOCKFILE is no longer around. Taking ownership of $LOCKFILE"
                release "FORCE"
                if (echo "$UUID" > "$LOCKFILE") 2>/dev/null; then
                    ACQUIRED="TRUE"
                    return 0
                else
                    echo "Cannot write to $LOCKFILE. Error." >&2
                    return 1
                fi
            fi
        else
            echo "Do you have write permissons to $LOCKFILE ?" >&2
            return 1
        fi
    fi
}

# Removes the lock file only if this script created it ($ACQUIRED is set), 
# OR, if we are removing a stale lock file (first parameter is "FORCE") 
release () {
    #Destroy lock file. Take no prisoners.
    if [ "$ACQUIRED" ] || [ "$1" == "FORCE" ]; then
        rm -f $LOCKFILE
    fi
}

# Test code
# int main( int argc, const char* argv[] )
echo "Acquring lock."
acquire
if [ $? -eq 0 ]; then 
    echo "Acquired lock."
    read -p "Press [Enter] key to release lock..."
    release
    echo "Released lock."
else
    echo "Unable to acquire lock."
fi

다른 해결책으로 +1을주었습니다. 대체로 AIX에서 작동하지 않습니다 (> ps -eo pid, ppid, lstart $$ | tail -1 ps : -o가있는 잘못된 목록). HP-UX가 아닙니다 (> ps -eo pid, ppid, lstart $$ tail -1 ps : 잘못된 옵션-o). 감사.
— Tagar

2

스크립트 시작 부분에이 줄을 추가하십시오

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

맨 무리의 상용구 코드입니다.

더 많은 로깅을 원하면이 것을 사용하십시오

[ "${FLOCKER}" != "$0" ] && { echo "Trying to start build from queue... "; exec bash -c "FLOCKER='$0' flock -E $E_LOCKED -en '$0' '$0' '$@' || if [ \"\$?\" -eq $E_LOCKED ]; then echo 'Locked.'; fi"; } || echo "Lock is free. Completing."

flock유틸리티를 사용하여 잠금을 설정하고 확인 합니다. 이 코드는 스크립트 이름으로 설정되지 않은 경우 FLOCKER 변수를 확인하여 처음으로 실행되었는지 감지 한 다음 Flocker 변수를 초기화하고 FLOCKER 변수가 초기화 된 상태에서 스크립트를 다시 재귀 적으로 시작하려고 시도합니다. 성공했고 계속해도 괜찮습니다. 잠금이 사용 중이면 구성 가능한 종료 코드로 실패합니다.

데비안 7에서는 작동하지 않는 것 같지만 실험적인 util-linux 2.25 패키지에서는 다시 작동하는 것 같습니다. "flock : ... Text file busy"라고 씁니다. 스크립트에 대한 쓰기 권한을 비활성화하여 재정의 할 수 있습니다.


1

PID 및 잠금 파일이 가장 안정적입니다. 프로그램을 실행하려고 할 때 잠금 파일이 있는지 확인하고, 존재하는 ps경우 프로세스가 여전히 실행 중인지 확인하는 데 사용할 수 있습니다 . 그렇지 않은 경우 스크립트가 시작되어 잠금 파일의 PID를 자체로 업데이트 할 수 있습니다.


1

bmdhack의 솔루션이 적어도 내 유스 케이스에서 가장 실용적인 것으로 나타났습니다. flock 및 lockfile을 사용하면 스크립트가 종료 될 때 rm을 사용하여 lockfile을 제거해야하므로 항상 보장 할 수는 없습니다 (예 : kill -9).

bmdhack의 솔루션에 대한 사소한 점 하나를 변경하려고합니다.이 세마포어의 안전한 작동에 필요하지 않다는 것을 말하지 않고 잠금 파일을 제거합니다. kill -0을 사용하면 사용 불능 프로세스에 대한 이전 잠금 파일이 무시되거나 덮어 쓰기됩니다.

따라서 단순화 된 솔루션은 다음을 싱글 톤 맨 위에 간단히 추가하는 것입니다.

## Test the lock
LOCKFILE=/tmp/singleton.lock 
if [ -e ${LOCKFILE} ] && kill -0 `cat ${LOCKFILE}`; then
    echo "Script already running. bye!"
    exit 
fi

## Set the lock 
echo $$ > ${LOCKFILE}

물론이 스크립트에는 잠금 테스트 및 설정 작업이 단일 원자 작업이 아니기 때문에 동시에 시작될 가능성이있는 프로세스에 경쟁 위험이 있다는 결점이 있습니다. 그러나 lhunath가 mkdir을 사용하도록 제안한 해결책은 종료 된 스크립트가 디렉토리 뒤에 남겨져 다른 인스턴스가 실행되지 못하게하는 결함이 있습니다.


1

semaphoric 실용 용도 flock(AS presto8 의해 예 상술)을 구현하는 카운팅 세마포어 . 원하는 특정 수의 동시 프로세스를 사용할 수 있습니다. 다양한 큐 워커 프로세스의 동시성 레벨을 제한하기 위해이를 사용합니다.

그것은 sem과 같지만 훨씬 가볍습니다. (전체 공개 : 나는 sem이 우리의 요구에 너무 무거워서 사용할 수있는 간단한 세마포어 유틸리티가 없다는 것을 알게 된 후에 썼다.)


1

flock (1)이지만 서브 쉘이없는 예. flock () 파일 / tmp / foo는 절대 제거되지 않지만 flock () 및 un-flock ()을 가져 오기 때문에 중요하지 않습니다.

#!/bin/bash

exec 9<> /tmp/foo
flock -n 9
RET=$?
if [[ $RET -ne 0 ]] ; then
    echo "lock failed, exiting"
    exit
fi

#Now we are inside the "critical section"
echo "inside lock"
sleep 5
exec 9>&- #close fd 9, and release lock

#The part below is outside the critical section (the lock)
echo "lock released"
sleep 5

1

외부 의존성이 필요없는 이미 백만 번이나 다른 방법으로 답변했습니다.

LOCK_FILE="/var/lock/$(basename "$0").pid"
trap "rm -f ${LOCK_FILE}; exit" INT TERM EXIT
if [[ -f $LOCK_FILE && -d /proc/`cat $LOCK_FILE` ]]; then
   // Process already exists
   exit 1
fi
echo $$ > $LOCK_FILE

현재 PID ($$)를 잠금 파일에 쓸 때마다 스크립트 시작시 프로세스가 최신 PID로 실행 중인지 확인합니다.


1
트랩 호출이 없으면 (또는 일반적인 경우에는 거의 마지막에 정리), 마지막 실행 후에 잠금 파일이 남아 있고 나중에 다른 프로세스에서 PID를 재사용하는 오 탐지 버그가 있습니다. (그리고 최악의 경우, 그것은 아파치와 같은 장기 실행 프로세스에 선물되었습니다 ....)
— Philippe Chaintreuil

1
내 접근 방식에 결함이 있으며 함정이 필요하다는 데 동의합니다. 내 솔루션을 업데이트했습니다. 나는 여전히 외부 의존성을 갖지 않는 것을 선호합니다.
— Filidor Wiese

1

프로세스의 잠금을 사용하는 것이 훨씬 강력하고 부실한 출구도 처리합니다. 프로세스가 실행되는 동안 lock_file은 열린 상태로 유지됩니다. 프로세스가 존재하면 (쉘이 죽더라도) 닫힙니다 (쉘로). 나는 이것이 매우 효율적이라는 것을 알았다.

lock_file=/tmp/`basename $0`.lock

if fuser $lock_file > /dev/null 2>&1; then
    echo "WARNING: Other instance of $(basename $0) running."
    exit 1
fi
exec 3> $lock_file 

1

스크립트의 맨 처음 @ oneliner를 사용합니다.

#!/bin/bash

if [[ $(pgrep -afc "$(basename "$0")") -gt "1" ]]; then echo "Another instance of "$0" has already been started!" && exit; fi
.
the_beginning_of_actual_script

프로세스의 상태에 관계없이 메모리에 프로세스가 있는지 확인하는 것이 좋습니다. 그러나 그것은 나를 위해 일을합니다.


0

무리의 길은 갈 길입니다. 스크립트가 갑자기 죽을 때 어떤 일이 발생하는지 생각해보십시오. 무리의 경우에는 무리를 풀어도 문제가되지 않습니다. 또한, 악의적 인 속임수는 스크립트 자체에 무리를 두는 것입니다. 그러나 당연히 권한 문제에 대해 전면적 인 조치를 취할 수 있습니다.


0

빠르고 더러운?

#!/bin/sh

if [ -f sometempfile ]
  echo "Already running... will now terminate."
  exit
else
  touch sometempfile
fi

..do what you want here..

rm sometempfile

7
사용 방법에 따라 문제가 될 수도 있고 아닐 수도 있지만 잠금 테스트와 생성간에 경쟁 조건이 있으므로 두 스크립트를 동시에 시작할 수 있습니다. 하나가 먼저 종료되면 다른 하나는 잠금 파일없이 계속 실행됩니다.
— TimB

3
휴대용 쉘 스크립팅에 대해 많은 것을 가르쳐 준 C News는 lock. $$ 파일을 만든 다음 "lock"과 연결하려고 시도했습니다. 그리고 종료했다.
— Paul Tomblin

문제가 발생하여 잠금 파일이 삭제되지 않은 경우 여전히 잠금 파일을 수동으로 제거해야한다는 점을 제외하고는 정말 좋은 방법입니다.
— Matthew Scharley

2
빠르고 더러운, 그것이 그가 요청한 것입니다 :)
— Aupajo
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.