두 번째 inotify 이벤트가 발생하면 프로세스를 종료하는 방법은 무엇입니까?

두 번째 inotify 이벤트가 발생하면 프로세스를 종료하는 방법은 무엇입니까?
inotifywait -q -m -e close_write,create --recursive ../orgmode-parse-print | 
while read -r filename event; do
    echo $filename;
    echo $event
    sleep infinity;
done

위의 문제는 영원히 "잠자기" 상태이며 절대 종료되지 않는다는 것입니다. sleep다른 이벤트가 발생하는 경우 프로세스(기본적으로 while 루프(포함)의 내용)를 종료하거나 다시 시작하려면 어떻게 해야 합니까 ?

즉, 명령을 실행하되 종료하고(인터럽트인 것으로 가정) 파일이 수정되면 다시 시작합니다.

sleep여기서는 이것을 예로 사용합니다. 실행 중인 실제 프로세스는 장기 실행 프로세스입니다 .

답변1

이것은 나에게 효과적입니다.

$ inotifywait -q -m -e close_write,create --recursive dir1/ | \
  ( 
    CNT=0; 
    while read -r filename event; do 
       echo "count: $CNT filename: $filename  event: $event"; ((CNT++)); 
       [ "$CNT" -eq 1 ] && exit; 
    done 
  )

먼저 샘플 디렉터리 구조를 만들었습니다.

$ mkdir -p dir1/dir2/dir{3..5}

$ tree dir1/
dir1/
└── dir2
    ├── afile
    ├── dir3
    ├── dir4
    └── dir5

4 directories, 1 file

그런 다음 이것을 실행하여 디렉토리 보기를 시작합니다.

$ inotifywait -q -m -e close_write,create --recursive dir1/ | ( CNT=0; while read -r filename event; do echo "count: $CNT filename: $filename  event: $event"; ((CNT++)); [ "$CNT" -eq 1 ] && exit; done )

그런 다음 touch afile이 디렉터리에서 명령을 실행합니다.

$ cd dir1/dir2
$ touch afile
$ touch afile

결과는 다음과 같습니다 inotifywait.

count: 0 filename: dir1/dir2/  event: CLOSE_WRITE,CLOSE afile

두 번째 "이벤트"에 도달하면 종료됩니다.

문제와 더 나은 해결책

(...while ...)echo파이프용 서브셸을 사용할 때의 한 가지 문제점은 두 번째 이벤트가 발생할 때 두 번째 메시지가 표시되지 않는다는 것입니다 . 문제 없습니다. 다음과 같이 간단하게 리팩터링할 수 있습니다.

$ CNT=0; while read -r filename event; do echo "count: $CNT filename: $filename  event: $event"; ((CNT++)); [ "$CNT" -eq 2 ] && break; done < <(inotifywait -q -m -e close_write,create --recursive dir1/)
count: 0 filename: dir1/dir2/  event: CLOSE_WRITE,CLOSE afile
count: 1 filename: dir1/dir2/  event: CLOSE_WRITE,CLOSE afile
$

확장:

$ CNT=0; \
  while read -r filename event; do \
    echo "count: $CNT filename: $filename  event: $event"; ((CNT++)); \
    [ "$CNT" -eq 2 ] && break; \
  done < <(inotifywait -q -m -e close_write,create --recursive dir1/)
count: 0 filename: dir1/dir2/  event: CLOSE_WRITE,CLOSE afile
count: 1 filename: dir1/dir2/  event: CLOSE_WRITE,CLOSE afile
$

백그라운드 작업이 있습니다

while ...루프 내부에서 차단 되는 작업이 있는 경우 를 도입하여 이를 종료한 다음 루프가 에서 처리할 trap수 있도록 백그라운드에 배치 할 수 있습니다 .while ...inotifywait

예:

$ cat ./notifier.bash
#!/bin/bash

trap 'kill $(jobs -p)' EXIT;

CNT=0
while read -r filename event; do
  sleep 1000 &
  echo "count: $CNT filename: $filename  event: $event"; ((CNT++))
  [ "$CNT" -eq 2 ] && break
done < <(inotifywait -q -m -e close_write,create --recursive dir1/)

실행 중:

$ ./notifier.bash
count: 0 filename: dir1/dir2/  event: CLOSE_WRITE,CLOSE afile
count: 1 filename: dir1/dir2/  event: CLOSE_WRITE,CLOSE afile
./notifier.bash: line 1: kill: (30301) - No such process

sleep그리고 백그라운드 프로세스의 남은 부분이 없습니다 .

$ ps -eaf|grep [s]leep
$

trap마지막 조정에 관해 여러분도 눈치채셨을 것입니다. 이렇게 하면 kill $(jobs -p)다음과 같이 화면에 쓰레기가 던져집니다. 때로는 다음과 같습니다.

./notifier.bash: line 1: kill: (30301) - No such process

다음과 같이 정리할 수 있습니다.

 trap 'kill $(jobs -p) > /dev/null 2>&1' EXIT;

이제 실행하면:

$ ./notifier.bash
count: 0 filename: dir1/dir2/  event: CLOSE_WRITE,CLOSE afile
count: 1 filename: dir1/dir2/  event: CLOSE_WRITE,CLOSE afile
$

인용하다

답변2

새로운 인터럽트가 수신될 때 코드가 수행 중인 모든 작업을 종료한다고 가정하면 다음 bash 스크립트가 이 목적에 적합합니다.

#!/bin/bash

my_function () {
   sleep infinity
}

declare -A cache
inotifywait -q -m -e close_write,create --recursive ../orgmode-parse-print |
while read -r filename event; do
    if [ "${cache[pid_my_function]}" ]; then kill "${cache[pid_my_function]}"; fi
    echo $filename
    echo $event
    my_function &
    cache[pid_my_function]=$!
done

기본적으로 스크립트는 sleep infinity함수가 호출될 때 별도의 프로세스로 실행될 수 있도록 긴 프로세스( 로 표시됨)를 함수 내부에 넣습니다 &. 이 명령은 $!나중에 새 인터럽트가 도착할 때 종료될 수 있도록 변수에 프로세스 번호를 인쇄합니다.


OBS: 이 스크립트는 새로운 인터럽트가 수신될 때 코드가 수행하는 작업을 종료하지만 실제로 그렇게 하려는지는 잘 모르겠습니다. 함수를 사용하면 프로세스를 종료하지 않고도 각 인터럽트 호출을 별도의 프로세스로 실행할 수 있으므로 &스크립트가 모든 인터럽트에서 실행되는지 확인할 수 있습니다.

관련 정보