쉘 스크립트에서 파일을 기다리는 방법은 무엇입니까?


15

/tmp호출 된 디렉토리 에 파일이 나타날 때까지 기다리는 쉘 스크립트를 작성하려고하는데 sleep.txt일단 프로그램이 발견되면 프로그램이 중지됩니다. 그렇지 않으면 파일을 찾을 때까지 프로그램이 절전 (일시 중단) 상태가되기를 원합니다 . 이제 테스트 명령을 사용한다고 가정합니다. 그래서, 같은

(if [ -f "/tmp/sleep.txt" ]; 
then stop 
   else sleep.)

저는 쉘 스크립트 작성에 익숙하지 않으며 어떤 도움이라도 대단히 감사합니다!


을보십시오 $MAILPATH.
mikeserv

답변:


22

Linux에서는 inotify 커널 서브 시스템을 사용하여 디렉토리에 파일이 나타날 때까지 효율적으로 대기 할 수 있습니다 .

while read i; do if [ "$i" = sleep.txt ]; then break; fi; done \
   < <(inotifywait  -e create,open --format '%f' --quiet /tmp --monitor)
# script execution continues ...

( <()출력 재 지정 구문에 대해 Bash를 가정 )

고정 시간 간격 폴링과 비교하여이 방법의 장점

while [ ! -f /tmp/sleep.txt ]; do sleep 1; done
# script execution continues ...

커널이 더 많이 잠들었다는 것입니다. create,open스크립트 와 같은 inotify 이벤트 사양을 사용하면 아래 파일을 /tmp만들거나 열 때 스크립트가 실행되도록 예약됩니다 . 고정 시간 간격 폴링을 사용하면 각 시간 단위로 CPU주기가 낭비됩니다.

파일이 이미 존재할 때 open등록 하는 이벤트도 포함 시켰습니다 touch /tmp/sleep.txt.


고마워, 이거 대단해! 파일 이름을 알고 있으면 왜 while 루프가 필요합니까? 왜 그렇게 할 수 없었 inotifywait -e create,open --format '%f' --quiet /tmp/sleep.txt --monitor > /dev/null ; # continues once /tmp/sleep.txt is created/opened습니까?
NHDaly

아, jk 도구를 설치 한 후 지금 이해합니다. inotifywait자체는 while 루프이며 파일을 열거 나 만들 때마다 줄을 출력합니다. 따라서 변경 될 때 중단하려면 while 루프가 필요합니다.
NHDaly

test-e / sleep 버전과 달리 경쟁 조건을 생성한다는 점을 제외하고. 루프가 입력되기 직전에 파일이 생성되지 않도록 보장 할 방법이 없습니다.이 경우 이벤트가 누락됩니다. 루프 앞에 (sleep 1; [[-e /tmp/sleep.txt]] && touch /tmp/sleep.txt;) & 같은 것을 추가해야합니다. 실제 비즈니스 로직에 따라 어떤 문제가 생길 수
n-alexander

@ n-alexander 글쎄, 대답은 sleep.txt특정 시점에 생성 될 프로세스를 시작하기 전에 스 니펫을 실행한다고 가정합니다 . 따라서 경쟁 조건이 없습니다. 이 질문에는 당신이 염두에 둔 시나리오에 대한 힌트가 포함되어 있지 않습니다.
maxschlepzig

@maxschlepzig와 반대로. 주문이 적용되지 않으면 가정 할 수 없습니다. 기초.
n-alexander

10

while루프에 테스트를 넣으십시오 .

while [ ! -f /tmp/sleep.txt ]; do sleep 1; done
# next command

파일을 작성하지 않아도 스크립트는 잠들게됩니다. 그렇지 않으면 완료됩니다. 옳은?
Mo Gainz

@MoGainz 맞습니다. 이후의 숫자 sleep는 각 반복에서 대기하는 시간 (초)입니다. 필요에 따라 변경할 수 있습니다.
jimmij

7

inotifywait지금까지 제공된 일부 기반 접근 방식 에는 몇 가지 문제점이 있습니다 .

  • sleep.txt먼저 임시 이름으로 작성된 파일 이름 을 찾지 못했습니다 sleep.txt. moved_to이벤트 이외에도 일치해야 합니다.create
  • 파일 이름에는 줄 바꿈 문자가 포함될 수 있으며, 줄 바꿈으로 구분 된 파일 이름을 인쇄하는 것만으로는 파일 sleep.txt이 만들어 졌는지 확인할 수 없습니다 . 뭐라고 경우 foo\nsleep.txt\nbar파일은 예를 들어 생성 된?
  • 파일이 시작되고 시계를 설치 하기 전에 파일이 생성되면 어떻게 inotifywait됩니까? 그런 다음 inotifywait이미 존재하는 파일을 영원히 기다립니다. 시계를 설치 한 후 파일이 없는지 확인해야합니다 .
  • inotifywait파일을 찾은 후 일부 솔루션이 실행됩니다 (적어도 다른 파일이 작성 될 때까지).

이를 해결하기 위해 다음을 수행 할 수 있습니다.

sh -c 'echo "$$" &&
        LC_ALL=C exec inotifywait -me create,moved_to --format=/%f/ . 2>&1' | {
  IFS= read pid &&
    while IFS= read -r line && [ "$line" != "Watches established." ]; do
      : wait for watches to be established
    done
  [ -e sleep.txt ] || [ -L sleep.txt ] || grep -qxF /sleep.txt/ && kill "$pid"
}

우리의 창조를 위해보고있는 것을 참고 sleep.txt, 현재 디렉토리에 .(당신이 할 거라고, 그래서 cd /tmp || exit귀하의 예제에서 전에). 현재 디렉토리는 변경되지 않으므로 해당 파이프 라인이 성공적으로 리턴되면 sleep.txt작성된 현재 디렉토리에 있습니다.

물론, 당신은 대체 할 수 ./tmp이상하지만, 동시에 inotifywait실행되고, /tmp여러 번 이름이 변경되었습니다 수 (대한 가능성 /tmp때문에 때 파이프 라인 반환, 그렇지 않을 수도 있지만, 뭔가 일반적인 경우에 고려해야 할) 새 파일 시스템은, 그 위에 장착 될 /tmp/sleep.txt만들어 졌는지하지만 /new-name-for-the-original-tmp/sleep.txt대신합니다. /tmp또한 간격에 따라 새 디렉토리를 작성할 수 있었으며 해당 디렉토리를 보지 않았으므로 작성된 디렉토리는 sleep.txt감지되지 않습니다.


1

허용 된 답변은 실제로 작동하지만 (maxschlepzig 덕분에) 스크립트가 종료 될 때까지 inotifywait 모니터링을 백그라운드로 둡니다. inotifywait에 의해 모니터링되는 디렉토리가 점 (.)에서 '/ tmp'로 변경되면 요구 사항 과 정확히 일치하는 유일한 대답 (예 : sleep.txt가 / tmp 내에 표시되기를 기다리는 것)은 Stephane의 것 같습니다.

그러나 임시 디렉토리를 사용하고자하는 경우 에만 귀하의 sleep.txt 플래그를 퍼팅과 단지 파일 작품이 충분하다이 디렉토리를 볼 inotifywait를 요청, 디렉토리의 파일을 넣어 다른 그 누구도 장담 할 수 없다 :

1 단계 : 모니터링 할 디렉토리를 만듭니다.

directoryToPutSleepFile=$(mktemp -d)

2 단계 : 디렉토리가 실제로 있는지 확인

until [ -d $directoryToPutSleepFile ]; do sleep 0.1; done

3 단계 : 모든 파일이 내부에 나타날 때까지 기다립니다 $directoryToPutSleepFile

inotifywait -e create --format '%f' --quiet $directoryToPutSleepFile

넣을 파일 $directoryToPutSleepFile이름은 sleep.txt awake.txt 일 수 있습니다. 순간 어떤 파일이 내부에 생성 $directoryToPutSleepFile스크립트는지나 계속 inotifywait문.


1
그러나 파일이 다른 파일이 바로 생성 된 경우 sleep.txt두 개의 inotifywait 호출 사이에 파일이 생성되는 경우 파일 생성이 누락 될 수 있습니다.
Stéphane Chazelas

그것을 해결하는 다른 방법 은 내 대답 을 참조하십시오 .
Stéphane Chazelas

내 코드를 시도하십시오. inotifywait는 한 번만 호출됩니다. sleep.txt가 작성되거나 이미 존재하는 경우 읽기 / 쓰기가되면 inotifywait는 "sleep.txt"를 출력하고 'done'명령문 이후에 실행이 계속됩니다.
nikolaos

호출 된 파일 sleep.txt이 작성 될 때까지 여러 번 호출됩니다 . 예를 들어보십시오 touch /tmp/foo /tmp/sleep.txt다음의 인스턴스 inotifywait의지 보고서 foo및 종료, 그리고 inotifywait를 그것의 생성을 감지하지 않도록 다음 inotifywait를 시작하는 시간의 sleep.txt 긴 이미 된 것입니다.
Stéphane Chazelas

/ tmp 아래에 작성된 모든 파일에 대해 한 번의 호출로 여러 호출에 대해 맞습니다. 내 답변을 업데이트했습니다. 귀하의 의견에 감사드립니다.
nikolaos

0

일반적으로 간단한 테스트 / 수면 루프 이외의 것을 안정적으로 만드는 것은 매우 어렵습니다. 주요 문제점은 테스트가 파일 작성과 경쟁한다는 점입니다. 테스트를 반복하지 않으면 작성 이벤트가 누락 될 수 있습니다. 셸을 사용하여 시작하는 대부분의 경우 가장 좋은 방법은 다음과 같습니다.

#!/bin/bash
while [[ ! -e /tmp/file ]] ; do
    sleep 1
done

성능 문제로 인해 이것이 불충분하다는 것을 알게되면 셸을 사용하는 것이 처음에는 좋은 생각이 아닙니다.


2
이것은 jimmij의 답변 과 중복 됩니다.
maxschlepzig

0

maxschlepzig의 승인 된 답변 (및 /superuser/270529/monitoring-a-file-until-a-string-is-found 에서 허용 된 답변의 아이디어)을 바탕으로 다음을 개선 할 것을 제안합니다 ( 내 견해로는) 시간 초과와 함께 작동 할 수있는 답변 :

# Enable pipefail, so if the left side of the pipe fails it does not get silently ignored
set -o pipefail
( timeout 120 inotifywait -e create,open --format '%f' --quiet /tmp --monitor & ) | while read i; do if [ "$i" == 'sleep.txt' ]; then break; fi; done
EXIT_STATUS=$?
if [ "${EXIT_STATUS}" == '124' ]; then
  echo "Timeout happened"
fi

주어진 시간 초과 내에 파일이 작성 / 열리지 않는 경우, 종료 상태는 124입니다 (시간 초과 문서 (man page)). 작성 / 열린 경우 종료 상태는 0 (성공)입니다.

예, inotifywait는 이런 방식으로 하위 셸에서 실행되며 시간 초과가 발생하거나 주 스크립트가 종료 될 때 (둘 중 먼저 오는 경우) 하위 셸의 실행이 완료됩니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.