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:当收到新的中断时,该脚本会终止您的代码正在执行的操作,但我不确定您是否真的想这样做。您可以使用函数将每个中断调用作为一个单独的进程运行&
,而不必终止该进程,因此您可以确保您的脚本在所有中断上执行...