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