如果发生第二个 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

上面的问题是它永远“休眠”并且永远不会终止。如果发生另一个事件,如何终止或重新启动进程(本质上是 while 循环的内容(包括sleep))?

换句话说,执行命令,但终止它(我想是中断它)并在文件被修改时重新启动。

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 ...)对管道使用子 shell 的一个问题是,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 ...循环内阻塞,您可以引入 atrap来终止它,然后将其置于后台以允许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:当收到新的中断时,该脚本会终止您的代码正在执行的操作,但我不确定您是否真的想这样做。您可以使用函数将每个中断调用作为一个单独的进程运行&,而不必终止该进程,因此您可以确保您的脚本在所有中断上执行...

相关内容