development/script

파이썬에서 터미널 지우기

big-blog 2020. 5. 24. 12:15
반응형

파이썬에서 터미널 지우기


이 질문에는 이미 답변이 있습니다.

파이썬 스크립트에서 터미널 화면을 지우는 표준 "배터리 포함"방법이 있습니까? 아니면 저주를 받아야합니까 (단어가 아닌 라이브러리)?


탈출 시퀀스는 어떻습니까?

print(chr(27) + "[2J")

단순하고 크로스 플랫폼 솔루션은

cls

Windows 또는

clear

Unix 시스템 에서 명령 을 사용하는 것 입니다. 와 함께

os.system

사용하면 멋진 라이너를 만들 수 있습니다.

import os
os.system('cls' if os.name == 'nt' else 'clear')

왜 단순히 Windows 에서 + 또는 Mac에서

Ctrl

+

L

수행하는 것에 대해 이야기하지 않은 사람이 있습니까 ? 화면을 지우는 가장 간단한 방법입니다.

CmdL


Linux / UNIX 시스템 인 경우 ANSI 이스케이프 시퀀스를 인쇄하여 화면을 지우면 작업이 수행됩니다. 커서를 화면 상단으로 이동하려고 할 수도 있습니다. 이것은 ANSI를 지원하는 모든 터미널에서 작동합니다.

import sys
sys.stderr.write("\x1b[2J\x1b[H")

ANSI 지원이 활성화되어 있지 않으면 Windows에서 작동하지 않습니다. Windows와 동등한 제어 시퀀스가있을 수 있지만 모르겠습니다.


Windows, Mac 및 Linux의 경우 다음 코드를 사용할 수 있습니다.

import subprocess, platform

if platform.system()=="Windows":
    subprocess.Popen("cls", shell=True).communicate() #I like to use this instead of subprocess.call since for multi-word commands you can just type it out, granted this is just cls and subprocess.call should work fine 
else: #Linux and Mac
    print("\033c", end="")

jamesnotjim

print("\033c", end="")

은 Mac을 테스트했으며 Linux 및 Windows에서 테스트했습니다 (Windows에서는 작동하지 않으므로을 호출하는 다른 코드

cls

). 내가 print ( "\ 033c") 및 / 또는 printf 버전을 사용한 것을 처음 본 사람이 기억 나지 않습니다

subprocess.Popen("printf '\033c'", shell=True).communicate()

.rolika는

end=""

나중에 새 줄을 인쇄하지 못하게 할 것이라고 지적 했습니다.


나에게 가장 우아한 변형은 다음과 같습니다.

import os
os.system('cls||clear')

명확하게 의존 할 수는 있지만 모든 Linux 배포판에서 사용하지 못할 수도 있습니다. Windows에서는 언급 한대로 cls를 사용하십시오.

import subprocess
import platform

def clear():
    subprocess.Popen( "cls" if platform.system() == "Windows" else "clear", shell=True)

clear()

참고 : 터미널 화면을 제어하는 ​​것은 잘못된 형식으로 간주 될 수 있습니다. 옵션 사용을 고려하고 있습니까? 사용자가 화면을 지 울지 여부를 결정하도록하는 것이 좋습니다.


순수한 파이썬 솔루션.

 

ANSI 또는 외부 명령에 의존하지 않습니다.

 

터미널 만 볼 수있는 라인 수를 알려줄 수 있어야합니다.

from shutil import get_terminal_size
print("\n" * get_terminal_size().lines, end='')

파이썬 버전> = 3.3.0


얼마 전에이 문제가 발생했습니다.

def clearscreen(numlines=100):
  """Clear the console.
numlines is an optional argument used only as a fall-back.
"""
# Thanks to Steven D'Aprano, http://www.velocityreviews.com/forums

  if os.name == "posix":
    # Unix/Linux/MacOS/BSD/etc
    os.system('clear')
  elif os.name in ("nt", "dos", "ce"):
    # DOS/Windows
    os.system('CLS')
  else:
    # Fallback for other operating systems.
    print('\n' * numlines)

그런 다음 clearscreen ()을 사용하십시오.


그냥 사용하십시오 :

print("\033c")

터미널 창이 지워집니다.


그래서 여기에 2 센트를 넣을 것이라고 생각했습니다.아무도 OP 질문에 대한 진정한 대답을 제공하지 않았습니다. 모두가 'NO DONT USE os.system () 사용하지 마십시오!' 설명없이 또는 새 줄 인쇄에 의존하는 솔루션을 제공합니다.어떤 이유로 든 터미널 화면을 지우고 뒤로 스크롤해야하는 경우 다음 코드를 사용할 수 있습니다.

import os

def clear():
    '''
    Clears the terminal screen and scroll back to present
    the user with a nice clean, new screen. Useful for managing
    menu screens in terminal applications.
    '''
    os.system('cls' if os.name == 'nt' else 'echo -e \\\\033c')

print('A bunch of garbage so we can garble up the screen...')
clear()

# Same effect, less characters...

def clear():
    '''
    Clears the terminal screen and scroll back to present
    the user with a nice clean, new screen. Useful for managing
    menu screens in terminal applications.
    '''
    os.system('cls||echo -e \\\\033c')

이것은 OP의 원하는 효과가 있습니다. os.system () 명령을 사용하므로 악의적이며 subprocess.call ()을 사용하여이를 구현하는 방법을 알고 있다면 하위 프로세스를 선호하지만 전혀 익숙하지 않으므로 의견을 말하십시오.


이것은 Python2 또는 Python3 버전 모두에서 작동합니다.

print (u"{}[2J{}[;H".format(chr(27), chr(27)))

이 함수는 기본적으로 ANSI 이스케이프 시퀀스를 인식하기 때문에 그놈 터미널에서 작동합니다. 그것은

rows_max

터미널의 바닥 에서뿐만 아니라 그것이 호출 된 곳에서 정확하게 CLEAN PROMPT 거리를 제공합니다 . 지우는 정도를 완벽하게 제어 할 수 있습니다.

def clear(rows=-1, rows_max=None, *, calling_line=True, absolute=None,
          store_max=[]):
    """clear(rows=-1, rows_max=None)
clear(0, -1) # Restore auto-determining rows_max
clear(calling_line=False) # Don't clear calling line
clear(absolute=5) # Absolutely clear out to 5 rows up"""
    from os import linesep
    if rows_max and rows_max != -1:
        store_max[:] = [rows_max, False]
    elif not store_max or store_max[1] or rows_max == -1 or absolute:
        try:
            from shutil import get_terminal_size
            columns_max, rows_max = get_terminal_size()
        except ImportError:
            columns_max, rows_max = 80, 24
        if absolute is None:
            store_max[:] = [rows_max, True]
    if store_max:
        if rows == -1:
            rows = store_max[0]
        elif isinstance(rows, float):
            rows = round(store_max[0] * rows)
        if rows > store_max[0] - 2:
            rows = store_max[0] - 2
    if absolute is None:
        s = ('\033[1A' + ' ' * 30 if calling_line else '') + linesep * rows
    else:
        s = '\033[{}A'.format(absolute + 2) + linesep
        if absolute > rows_max - 2:
            absolute = rows_max - 2
        s += (' ' * columns_max + linesep) * absolute + ' ' * columns_max
        rows = absolute
    print(s + '\033[{}A'.format(rows + 1))

이행:

clear() # Clear all, TRIES to automatically get terminal height
clear(800, 24) # Clear all, set 24 as terminal (max) height
clear(12) # Clear half of terminal below if 24 is its height
clear(1000) # Clear to terminal height - 2 (24 - 2)
clear(0.5) # float factor 0.0 - 1.0 of terminal height (0.5 * 24 = 12)
clear() # Clear to rows_max - 2 of user given rows_max (24 - 2)
clear(0, 14) # Clear line, reset rows_max to half of 24 (14-2)
clear(0) # Just clear the line
clear(0, -1) # Clear line, restore auto-determining rows_max
clear(calling_line=False) # Clear all, don't clear calling line
clear(absolute=5) # Absolutely clear out to 5 rows up

매개 변수 :

rows

터미널의 프롬프트와 하단 사이에 추가 할 일반 텍스트 행의 수이며 모든 것을 위로 밀어냅니다.

rows_max

는 텍스트 행의 터미널 높이 (또는 최대 지우기 높이)이며 한 번만 설정하면되지만 언제든지 재설정 할 수 있습니다.

*,

세 번째 매개 변수 위치에서 다음의 모든 매개 변수는 키워드로만 사용됩니다 (예 : clear (absolute = 5)).

calling_line=True

(기본값)은 대화식 모드에서 더 잘 작동합니다.

calling_line=False

텍스트 기반 터미널 응용 프로그램에 더 적합합니다.

absolute

터미널 크기를 줄인 후 대화식 모드에서 글리치 갭 문제를 해결하기 위해 추가되었지만 터미널 응용 프로그램에도 사용할 수 있습니다.

store_max

비밀의 "지속적인"

rows_max

가치 저장을위한 것입니다 . 이 매개 변수를 명시 적으로 사용하지 마십시오. (에 인수가 전달되지 않은 경우

store_max

의 목록 내용을

store_max

변경하면이 매개 변수의 기본값이 변경됩니다. 따라서 영구 저장소입니다.)이식성 : 죄송합니다. 유휴 상태에서는 작동하지 않지만 ANSI 이스케이프 시퀀스를 인식하는 터미널 (콘솔)의 대화식 모드에서는 >> VERY COOL <<가 작동합니다. 나는 gnome-terminal에서 Python 3.3을 사용하여 Ubuntu 13.10에서만 이것을 테스트했습니다. 따라서 이식성은 Python 3.3 (

shutil.get_terminal_size()

BEST 결과 함수) 및 ANSI 인식 에만 의존한다고 가정 할 수 있습니다 .

print(...)

함수는 Python 3입니다. 또한 간단한 텍스트 기반 터미널 Tic Tac Toe 게임 (응용 프로그램)으로 테스트했습니다.

대화식 모드에서 사용하려면 :

먼저 대화식 모드 에서

copy(...)

기능을 복사하여 붙여넣고 작동하는지 확인하십시오. 그렇다면 위 함수를 clear.py 파일에 넣으십시오. 터미널에서 'python3'으로 파이썬을 시작하십시오. 시작하다:

>>> import sys
>>> sys.path
['', '/usr/lib/python3.3', ...

이제 clear.py 파일을

path

나열된 디렉토리 중 하나에 놓아서 파이썬이 찾을 수 있도록하십시오 (기존 파일을 덮어 쓰지 마십시오). 지금부터 쉽게 사용하려면 :

>>> from clear import clear
>>> clear()
>>> print(clear.__doc__)
clear(rows=-1, rows_max=None)
clear(0, -1) # Restore auto-determining rows_max
clear(calling_line=False) # Don't clear calling line
clear(absolute=5) # Absolutely clear out to 5 rows up

터미널 응용 프로그램에서 사용하는 경우 :

기본 .py 파일

copy(...)

과 같은 폴더에있는 clear.py 파일에 함수를 넣습니다 . 다음은 Tic Tac Toe 게임 응용 프로그램 (터미널 프롬프트에서 실행 : python3 tictactoe .py) 의 작업 개요 (골격) 예제입니다 .

from os import linesep

class TicTacToe:    
    def __init__(self):
        # Clear screen, but not calling line
        try:
            from clear import clear
            self.clear = clear
            self.clear(calling_line=False)
        except ImportError:
            self.clear = False
        self.rows = 0    # Track printed lines to clear

        # ...
        self.moves = [' '] * 9

    def do_print(self, *text, end=linesep):
        text = list(text)
        for i, v in enumerate(text[:]):
            text[i] = str(v)
        text = ' '.join(text)
        print(text, end=end)
        self.rows += text.count(linesep) + 1

    def show_board(self):
        if self.clear and self.rows:
            self.clear(absolute=self.rows)
        self.rows = 0
        self.do_print('Tic Tac Toe')
        self.do_print('''   |   |
 {6} | {7} | {8}
   |   |
-----------
   |   |
 {3} | {4} | {5}
   |   |
-----------
   |   |
 {0} | {1} | {2}
   |   |'''.format(*self.moves))

    def start(self):
        self.show_board()
        ok = input("Press <Enter> to continue...")
        self.moves = ['O', 'X'] * 4 + ['O']
        self.show_board()
        ok = input("Press <Enter> to close.")

if __name__ == "__main__":
    TicTacToe().start()

Explanation: do_print(...) on line 19 is a version of print(...) needed to keep track of how many new lines have been printed (self.rows). Otherwise, you would have to self.rows += 1 all over the place where print(...) is called throughout the entire program. So each time the board is redrawn by calling show_board() the previous board is cleared out and the new board is printed exactly where it should be. Notice self.clear(calling_line=False) on line 9 basically pushes everything up RELATIVE to the bottom of the terminal, but does not clear the original calling line. In contrast, self.clear(absolute=self.rows) on line 29 absolutely clears out everything self.rows distance upward, rather than just pushing everything upward relative to the bottom of the terminal.

Ubuntu users with Python 3.3: Put #!/usr/bin/env python3 on the very first line of the tictactoe.py file. Right click on the tictactoe.py file => Properties => Permissions tab => Check Execute: Allow executing file as program. Double click on the file => Click Run in Terminal button. If an open terminal's current directory is that of the tictactoe.py file, you can also start the file with ./tictactoe.py.


If you wish to clear your terminal when you are using a python shell. Then, you can do the following to clear the screen

import os
os.system('clear')

You could tear through the terminfo database, but the functions for doing so are in curses anyway.


python -c "from os import system; system('clear')"


You can use call() function to execute terminal's commands :

from subprocess import call
call("clear")

you can make your own. this will not be dependent on your terminal, or OS type.

def clear(num):
    for i in range(num): print 

clear(80)
print "hello"

This will clear 25 new lines:

def clear():
    print(' \n' * 25)

clear()

I use eclipse with pydev. I like the newline solution better than the for num in range . The for loop throws warnings, while the print newline doesn't. If you want to specify the number of newlines in the clear statement try this variation.

def clear(j):
    print(' \n' * j)

clear(25)

If all you need is to clear the screen, this is probably good enough. The problem is there's not even a 100% cross platform way of doing this across linux versions. The problem is the implementations of the terminal all support slightly different things. I'm fairly sure that "clear" will work everywhere. But the more "complete" answer is to use the xterm control characters to move the cursor, but that requires xterm in and of itself.

Without knowing more of your problem, your solution seems good enough.


A perhaps cheesy way to clear the screen, but one that will work on any platform I know of, is as follows:

for i in xrange(0,100):
    print ""

I would do it in this way to make it look more like bash:

Just create a file named .pythonstartup at Home directory and use poke's answer in a function

On Linux:

echo "from subprocess import call
def clear(int=None):  
    call('clear')
    if int == 0:
       exit()
clear()" >> $HOME/.pythonstartup ; export PYTHONSTARTUP=$HOME/.pythonstartup ; python

You can add export PYTHONSTARTUP=$HOME/.pythonstartup to your ./bashrc file

Since what I care about is space; a call to the function will not display the python interpreter description at startup, but you can remove clear() to retain it.

Using it like a normal function should do the trick without printing the exit status:

>>> clear()

If you pass the argument 0 to the function it will clear the screen and exit successfully so you can continue using the shell in a clean screen

>>> clear(0)

For Windows, on the interpreter command line only (not the GUI)! Simply type: (Remember to use proper indentation with python):

import os
def clear():
    os.system('cls')

Every time you type clear() on the shell (command line), it will clear the screen on your shell. If you exit the shell, then you must redo the above to do it again as you open a new Python (command line) shell.

Note: Does not matter what version of Python you are using, explicitly (2.5, 2.7, 3.3 & 3.4).


The accepted answer is a good solution. The problem with it is that so far it only works on Windows 10, Linux and Mac. Yes Windows (known for it lack of ANSI support)! This new feature was implemented on Windows 10 (and above) which includes ANSI support, although you have to enable it. This will clear the screen in a cross platform manner:

import os

print ('Hello World')
os.system('') 
print ("\x1B[2J")

On anything below Windows 10 however it returns this:

[2J

This is due to the lack of ANSI support on previous Windows builds. This can however, be solved using the colorama module. This adds support for ANSI characters on Windows:

ANSI escape character sequences have long been used to produce colored terminal text and cursor positioning on Unix and Macs. Colorama makes this work on Windows, too, by wrapping stdout, stripping ANSI sequences it finds (which would appear as gobbledygook in the output), and converting them into the appropriate win32 calls to modify the state of the terminal. On other platforms, Colorama does nothing.

So here is a cross platform method:

import sys

if sys.platform == 'win32':
    from colorama import init
    init()

print('Hello World')

print("\x1B[2J")

Or print(chr(27) + "[2J") used instead of print("\x1B[2J").


@poke answer is very insecure on Windows, yes it works but it is really a hack. A file named cls.bat or cls.exe in the same dictionary as the script will conflict with the command and execute the file instead of the command, creating a huge security hazard.

One method to minimise the risk could be to change the location of where the cls command is called:

import os
os.system('cd C:\\Windows|cls' if os.name == 'nt' else 'clear')

This will change the Currant Dictionary to C:\Window (backslash is important here) then execute. C:\Windows is always present and needs administration permissions to write there making it a good for executing this command with minimal risk. Another solution is to run the command through PowerShell instead of Command Prompt since it has been secured against such vulnerabilities.

There are also other methods mentioned in this question: Clear screen in shell which may also be of use.


In Windows you can use:

>>> import os
>>> clear = lambda: os.system('cls')
>>> clear()

By default, os.system("clear")/os.system("cls") will return an int type as 0. We can completely clear the screen by assigning it to a variable and deleting that.

def clear():
    if (os.name == 'nt'):    
        c = os.system('cls')
    else:
        c = os.system('clear')
    del c  # can also omit c totally

#clear()

This works on all platforms and it does work in both Python 2 and 3.

def clear(number):
    for i in range(number):
        print(" ")

Then to clear just type clear(numberhere).

참고URL : https://stackoverflow.com/questions/2084508/clear-terminal-in-python

UICollectionView 간격 여백다른 객체에서 객체 속성을 복제하는 방법?글라이드 라이브러리로 이미지를 반올림하는 방법은 무엇입니까?Maven 프로젝트 버전을 bash 명령 줄로 가져 오는 방법XAMPP-PID 4에서 "프로세스를 열 수 없음"에서 사용중인 포트 80!

한 번에 하나의 셸 스크립트 인스턴스 만 실행되도록하는 빠르UICollectionView 간격 여백고 더러운 방법</fo다른 객체에서 객체 속성을 복제하는 방법?nt>


주어진 시간에 하나의 쉘 스크립트 인스턴스 만 실행되도록하는 가장 빠르고 더러운 방법은 무엇입니까?


다음은

잠금 파일

을 사용 하고 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

).


사용은

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 이외의 운영 체제를 실행하는 경우 운영 체제가 사용 가능하지 않을 수 있습니다.


"파일 잠금"의 존재를 테스트하는 모든 접근 방식에는 결함이 있습니다.왜? 파일이 존재하는지 확인하고 단일 원자 조치로 파일을 작성할 수있는 방법이 없기 때문입니다. 이것 때문에; 경쟁 조건이

것입니다

상호 배제 휴식에 당신의 시도를이.대신을 사용해야

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)도

있습니다.


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

대한

예제 가 매뉴얼 페이지 에 있습니다.


무리와 같은 원자 연산이 필요합니다. 그렇지 않으면 결국 실패합니다.그러나 무리를 사용할 수 없으면 어떻게해야합니까? mkdir이 있습니다. 그것은 원자 작업입니다. 하나의 프로세스 만 mkdir을 성공적으로 수행하고 다른 프로세스는 모두 실패합니다.코드는 다음과 같습니다.

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

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


잠금을 안정적으로 만들려면 원 자성 작업이 필요합니다. 위의 제안 중 많은 부분이 원자가 아닙니다. 제안 된 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

제거도 원자가 아니므로 먼저 원자 이름을 변경 한 다음 제거하십시오.symlink 및 이름 바꾸기 호출의 경우 두 파일 이름이 모두 동일한 파일 시스템에 있어야합니다. 내 제안 : 간단한 파일 이름 (경로 없음) 만 사용하고 파일을 넣고 동일한 디렉토리에 고정하십시오.


또 다른 옵션은을

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

 

GNU Parallel

로 호출 할 때 뮤텍스로 작동하므로 이것을 사용할 수 있습니다

sem

. 따라서 구체적인 용어로 다음을 사용할 수 있습니다.

sem --id SCRIPTSINGLETON yourScript

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

sem --id SCRIPTSINGLETON --semaphoretimeout -10 yourScript

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

--id

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

GNU Parallel

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


셸 스크립트의 경우 잠금을 더 이식 가능하게 만들면서

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까지의 숫자를 기대하거나 갖습니다.이를 다른 것으로 설정하면 스크립트 및 배치 스트림이 이전 배치 작업 또는 스크립트에 따라 반응하도록 할 수 있습니다.


정말

빠르고

정말

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

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

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


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


원자 디렉토리 잠금과 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

이 예제는 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로 프로세스를 나열해야합니다. 숫자와 비교하십시오. 복잡하고 불확실하다


데비안 머신을 대상으로 할 때

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
}

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

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

일부 유닉스는

lockfile

이미 언급 한 것과 매우 유사합니다

flock

.맨 페이지에서 :

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

잠금 파일, lockdir, 특수 잠금 프로그램을 제거하고

pidof

모든 Linux 설치에서 찾을 수 없기 때문에 제거하고 싶었습니다 . 또한 가능한 한 가장 간단한 코드 (또는 가능한 한 적은 수의 행)를 원했습니다.

if

한 줄로 가장 간단한 진술 :

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

게시 된 기존 답변은 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 환경에서 위의 것을 사용했다.


실제로 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 명령을 포함하지 않았습니다. 이것이 완벽한 솔루션입니다


오래된 잠금 파일을 처리하는 간단한 방법을 사용합니다.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

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

[ "${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"라고 씁니다. 스크립트에 대한 쓰기 권한을 비활성화하여 무시할 수 있습니다.


PID 및 잠금 파일이 가장 안정적입니다. 프로그램을 실행하려고 할 때 잠금 파일이 있는지 확인하고, 존재하는

ps

경우 프로세스가 여전히 실행 중인지 확인하는 데 사용할 수 있습니다 . 그렇지 않은 경우 스크립트가 시작되어 잠금 파일의 PID를 자체로 업데이트 할 수 있습니다.


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을 사용하도록 제안한 해결책은 종료 된 스크립트가 디렉토리 뒤에 남겨져 다른 인스턴스가 실행되지 못하게하는 결함이 있습니다.


 

semaphoric

실용 용도

flock

(AS presto8 의해 예 상술)을 구현하는

카운팅 세마포어

. 원하는 특정 수의 동시 프로세스를 사용할 수 있습니다. 다양한 큐 워커 프로세스의 동시성 레벨을 제한하기 위해이를 사용합니다.그것은

sem과

같지만

훨씬

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


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

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

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로 실행 중인지 확인합니다.


프로세스의 잠금을 사용하는 것이 훨씬 강력하고 부실한 출구도 처리합니다. 프로세스가 실행되는 동안 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 

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


빠르고 더러운?

#!/bin/sh

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

..do what you want here..

rm sometempfile

FLOM (Free LOck Manager)

http://sourceforge.net/projects/flom/을

살펴보십시오 . 파일 시스템에서 잠금 파일이 필요없는 추상 자원을 사용하여 명령 및 / 또는 스크립트를 동기화 할 수 있습니다. NFS (Network File System) 서버와 같은 NAS (Network Attached Storage)없이 다른 시스템에서 실행중인 명령을 동기화 할 수 있습니다.가장 간단한 사용 사례를 사용하면 "command1"및 "command2"를 직렬화하는 것이 실행하는 것만 큼 쉽습니다.

flom -- command1

flom -- command2

두 개의 다른 쉘 스크립트에서.참고 URL :

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

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


주어진 시간에 하나의 쉘 스크립트 인스턴스 만 실행되도록하는 가장 빠르고 더러운 방법은 무엇입니까?


다음은

잠금 파일

을 사용 하고 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

).


사용은

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 이외의 운영 체제를 실행하는 경우 운영 체제가 사용 가능하지 않을 수 있습니다.


"파일 잠금"의 존재를 테스트하는 모든 접근 방식에는 결함이 있습니다.왜? 파일이 존재하는지 확인하고 단일 원자 조치로 파일을 작성할 수있는 방법이 없기 때문입니다. 이것 때문에; 경쟁 조건이

것입니다

상호 배제 휴식에 당신의 시도를이.대신을 사용해야

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)도

있습니다.


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

대한

예제 가 매뉴얼 페이지 에 있습니다.


무리와 같은 원자 연산이 필요합니다. 그렇지 않으면 결국 실패합니다.그러나 무리를 사용할 수 없으면 어떻게해야합니까? mkdir이 있습니다. 그것은 원자 작업입니다. 하나의 프로세스 만 mkdir을 성공적으로 수행하고 다른 프로세스는 모두 실패합니다.코드는 다음과 같습니다.

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

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


잠금을 안정적으로 만들려면 원 자성 작업이 필요합니다. 위의 제안 중 많은 부분이 원자가 아닙니다. 제안 된 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

제거도 원자가 아니므로 먼저 원자 이름을 변경 한 다음 제거하십시오.symlink 및 이름 바꾸기 호출의 경우 두 파일 이름이 모두 동일한 파일 시스템에 있어야합니다. 내 제안 : 간단한 파일 이름 (경로 없음) 만 사용하고 파일을 넣고 동일한 디렉토리에 고정하십시오.


또 다른 옵션은을

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

 

GNU Parallel

로 호출 할 때 뮤텍스로 작동하므로 이것을 사용할 수 있습니다

sem

. 따라서 구체적인 용어로 다음을 사용할 수 있습니다.

sem --id SCRIPTSINGLETON yourScript

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

sem --id SCRIPTSINGLETON --semaphoretimeout -10 yourScript

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

--id

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

GNU Parallel

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


셸 스크립트의 경우 잠금을 더 이식 가능하게 만들면서

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까지의 숫자를 기대하거나 갖습니다.이를 다른 것으로 설정하면 스크립트 및 배치 스트림이 이전 배치 작업 또는 스크립트에 따라 반응하도록 할 수 있습니다.


정말

빠르고

정말

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

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

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


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


원자 디렉토리 잠금과 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

이 예제는 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로 프로세스를 나열해야합니다. 숫자와 비교하십시오. 복잡하고 불확실하다


데비안 머신을 대상으로 할 때

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
}

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

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

일부 유닉스는

lockfile

이미 언급 한 것과 매우 유사합니다

flock

.맨 페이지에서 :

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

잠금 파일, lockdir, 특수 잠금 프로그램을 제거하고

pidof

모든 Linux 설치에서 찾을 수 없기 때문에 제거하고 싶었습니다 . 또한 가능한 한 가장 간단한 코드 (또는 가능한 한 적은 수의 행)를 원했습니다.

if

한 줄로 가장 간단한 진술 :

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

게시 된 기존 답변은 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 환경에서 위의 것을 사용했다.


실제로 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 명령을 포함하지 않았습니다. 이것이 완벽한 솔루션입니다


오래된 잠금 파일을 처리하는 간단한 방법을 사용합니다.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

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

[ "${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"라고 씁니다. 스크립트에 대한 쓰기 권한을 비활성화하여 무시할 수 있습니다.


PID 및 잠금 파일이 가장 안정적입니다. 프로그램을 실행하려고 할 때 잠금 파일이 있는지 확인하고, 존재하는

ps

경우 프로세스가 여전히 실행 중인지 확인하는 데 사용할 수 있습니다 . 그렇지 않은 경우 스크립트가 시작되어 잠금 파일의 PID를 자체로 업데이트 할 수 있습니다.


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을 사용하도록 제안한 해결책은 종료 된 스크립트가 디렉토리 뒤에 남겨져 다른 인스턴스가 실행되지 못하게하는 결함이 있습니다.


 

semaphoric

실용 용도

flock

(AS presto8 의해 예 상술)을 구현하는

카운팅 세마포어

. 원하는 특정 수의 동시 프로세스를 사용할 수 있습니다. 다양한 큐 워커 프로세스의 동시성 레벨을 제한하기 위해이를 사용합니다.그것은

sem과

같지만

훨씬

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


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

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

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로 실행 중인지 확인합니다.


프로세스의 잠금을 사용하는 것이 훨씬 강력하고 부실한 출구도 처리합니다. 프로세스가 실행되는 동안 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 

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


빠르고 더러운?

#!/bin/sh

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

..do what you want here..

rm sometempfile

FLOM (Free LOck Manager)

http://sourceforge.net/projects/flom/을

살펴보십시오 . 파일 시스템에서 잠금 파일이 필요없는 추상 자원을 사용하여 명령 및 / 또는 스크립트를 동기화 할 수 있습니다. NFS (Network File System) 서버와 같은 NAS (Network Attached Storage)없이 다른 시스템에서 실행중인 명령을 동기화 할 수 있습니다.가장 간단한 사용 사례를 사용하면 "command1"및 "command2"를 직렬화하는 것이 실행하는 것만 큼 쉽습니다.

flom -- command1

flom -- command2

두 개의 다른 쉘 스크립트에서.참고 URL :

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

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


주어진 시간에 하나의 쉘 스크립트 인스턴스 만 실행되도록하는 가장 빠르고 더러운 방법은 무엇입니까?


다음은

잠금 파일

을 사용 하고 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

).


사용은

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 이외의 운영 체제를 실행하는 경우 운영 체제가 사용 가능하지 않을 수 있습니다.


"파일 잠금"의 존재를 테스트하는 모든 접근 방식에는 결함이 있습니다.왜? 파일이 존재하는지 확인하고 단일 원자 조치로 파일을 작성할 수있는 방법이 없기 때문입니다. 이것 때문에; 경쟁 조건이

것입니다

상호 배제 휴식에 당신의 시도를이.대신을 사용해야

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)도

있습니다.


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

대한

예제 가 매뉴얼 페이지 에 있습니다.


무리와 같은 원자 연산이 필요합니다. 그렇지 않으면 결국 실패합니다.그러나 무리를 사용할 수 없으면 어떻게해야합니까? mkdir이 있습니다. 그것은 원자 작업입니다. 하나의 프로세스 만 mkdir을 성공적으로 수행하고 다른 프로세스는 모두 실패합니다.코드는 다음과 같습니다.

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

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


잠금을 안정적으로 만들려면 원 자성 작업이 필요합니다. 위의 제안 중 많은 부분이 원자가 아닙니다. 제안 된 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

제거도 원자가 아니므로 먼저 원자 이름을 변경 한 다음 제거하십시오.symlink 및 이름 바꾸기 호출의 경우 두 파일 이름이 모두 동일한 파일 시스템에 있어야합니다. 내 제안 : 간단한 파일 이름 (경로 없음) 만 사용하고 파일을 넣고 동일한 디렉토리에 고정하십시오.


또 다른 옵션은을

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

 

GNU Parallel

로 호출 할 때 뮤텍스로 작동하므로 이것을 사용할 수 있습니다

sem

. 따라서 구체적인 용어로 다음을 사용할 수 있습니다.

sem --id SCRIPTSINGLETON yourScript

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

sem --id SCRIPTSINGLETON --semaphoretimeout -10 yourScript

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

--id

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

GNU Parallel

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


셸 스크립트의 경우 잠금을 더 이식 가능하게 만들면서

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까지의 숫자를 기대하거나 갖습니다.이를 다른 것으로 설정하면 스크립트 및 배치 스트림이 이전 배치 작업 또는 스크립트에 따라 반응하도록 할 수 있습니다.


정말

빠르고

정말

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

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

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


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


원자 디렉토리 잠금과 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

이 예제는 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로 프로세스를 나열해야합니다. 숫자와 비교하십시오. 복잡하고 불확실하다


데비안 머신을 대상으로 할 때

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
}

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

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

일부 유닉스는

lockfile

이미 언급 한 것과 매우 유사합니다

flock

.맨 페이지에서 :

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

잠금 파일, lockdir, 특수 잠금 프로그램을 제거하고

pidof

모든 Linux 설치에서 찾을 수 없기 때문에 제거하고 싶었습니다 . 또한 가능한 한 가장 간단한 코드 (또는 가능한 한 적은 수의 행)를 원했습니다.

if

한 줄로 가장 간단한 진술 :

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

게시 된 기존 답변은 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 환경에서 위의 것을 사용했다.


실제로 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 명령을 포함하지 않았습니다. 이것이 완벽한 솔루션입니다


오래된 잠금 파일을 처리하는 간단한 방법을 사용합니다.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

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

[ "${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"라고 씁니다. 스크립트에 대한 쓰기 권한을 비활성화하여 무시할 수 있습니다.


PID 및 잠금 파일이 가장 안정적입니다. 프로그램을 실행하려고 할 때 잠금 파일이 있는지 확인하고, 존재하는

ps

경우 프로세스가 여전히 실행 중인지 확인하는 데 사용할 수 있습니다 . 그렇지 않은 경우 스크립트가 시작되어 잠금 파일의 PID를 자체로 업데이트 할 수 있습니다.


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을 사용하도록 제안한 해결책은 종료 된 스크립트가 디렉토리 뒤에 남겨져 다른 인스턴스가 실행되지 못하게하는 결함이 있습니다.


 

semaphoric

실용 용도

flock

(AS presto8 의해 예 상술)을 구현하는

카운팅 세마포어

. 원하는 특정 수의 동시 프로세스를 사용할 수 있습니다. 다양한 큐 워커 프로세스의 동시성 레벨을 제한하기 위해이를 사용합니다.그것은

sem과

같지만

훨씬

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


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

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

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로 실행 중인지 확인합니다.


프로세스의 잠금을 사용하는 것이 훨씬 강력하고 부실한 출구도 처리합니다. 프로세스가 실행되는 동안 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 

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


빠르고 더러운?

#!/bin/sh

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

..do what you want here..

rm sometempfile

FLOM (Free LOck Manager)

http://sourceforge.net/projects/flom/을

살펴보십시오 . 파일 시스템에서 잠금 파일이 필요없는 추상 자원을 사용하여 명령 및 / 또는 스크립트를 동기화 할 수 있습니다. NFS (Network File System) 서버와 같은 NAS (Network Attached Storage)없이 다른 시스템에서 실행중인 명령을 동기화 할 수 있습니다.가장 간단한 사용 사례를 사용하면 "command1"및 "command2"를 직렬화하는 것이 실행하는 것만 큼 쉽습니다.

flom -- command1

flom -- command2

두 개의 다른 쉘 스크립트에서.참고 URL :

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

반응형