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

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

/tmp호출 디렉터리 에 파일이 나타날 때까지 기다리는 쉘 스크립트를 작성하려고 합니다. sleep.txt파일이 발견되면 프로그램이 중지됩니다. 그렇지 않으면 파일을 찾을 때까지 프로그램이 잠자기 상태(중지)가 되도록 하고 싶습니다. 이제 test 명령을 사용한다고 가정하겠습니다. 그처럼

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

저는 쉘 스크립트 작성이 처음인데 도움을 주시면 대단히 감사하겠습니다!

답변1

Linux에서는 다음을 사용할 수 있습니다.inotify커널 하위 시스템은 디렉터리에 파일이 있을 때까지 효과적으로 기다립니다.

inotifywait  -e create,moved_to,attrib --include '/sleep.txt$' -qq /tmp
# script execution continues ...

참고: 파일이 이미 존재하는 경우에도 등록되도록 attrib이벤트를 추가했습니다 .touch /tmp/sleep.txt


sleep.txt가 다음과 같이 표시되는 경우inotify기다려즉, 시계가 빌드되기 전에 파일이 생성되거나 터치될 수 있고 이후에는 다시는 호출되지 않는 경우 다음과 같이 코드를 확장할 수 있습니다.

coproc inw {
    LC_ALL=C inotifywait  -e create,moved_to --include '/sleep.txt$'  /tmp 2>&1
}
while IFS= read -r -u "${inw[0]}" line; do
    if [ "$line" = "Watches established." ]; then
        break
    fi
done
if [ -f /tmp/sleep.txt ]; then
    kill $inw_PID
else
    wait
fi
# script execution continues ...

고정 간격 폴링과 비교한 이 접근 방식의 장점

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

더 많이자는 것은 당신의 과정입니다. inotify 이벤트 사양(예: 스크립트)을 사용하여 create,attrib다음 파일이 생성되거나 열릴 때만 실행되도록 예약합니다. /tmp고정된 간격으로 폴링하면 시간이 증가할 때마다 CPU 주기가 낭비됩니다.

답변2

테스트를 while루프에 넣어보세요.

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

답변3

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생성된 현재 디렉터리가 됩니다.

물론 위와 .같이 바꿀 수 있지만 런타임 시 이름이 여러 번 바뀌거나(가능성은 낮지 만 일반적인 경우 고려해야 할 사항) 그 위에 새 파일 시스템이 마운트되었을 수 있으므로 파이프가 반환될 때 그렇지 않을 수도 있습니다. 창조된 것이 되되 하나는 . 이 시간 동안 새 디렉터리가 생성되었고 해당 디렉터리가 모니터링되지 않아 거기에 생성된 디렉터리가 감지되지 않을 수도 있습니다 ./tmpinotifywait/tmp/tmp/tmp/sleep.txt/new-name-for-the-original-tmp/sleep.txt/tmpsleep.txt

답변4

maxschlepzig의 허용된 답변(및 허용된 답변의 아이디어)을 기반으로 합니다.https://superuser.com/questions/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 페이지)에 따라). 생성/열린 경우 종료 상태는 0(성공)입니다.

예, inotifywait는 이런 방식으로 하위 쉘에서 실행되며 하위 쉘은 시간 초과가 발생하거나 기본 스크립트가 종료되는 경우에만 실행이 완료됩니다.

관련 정보