为什么这个 inotifywait shellscript 购买了两个 PID?

tkj*_*kja 1 unix linux bash inotifywait

我正在学习使用 inotifywait,特别是通过使用以下脚本:https ://unix.stackexchange.com/questions/24952/script-to-monitor-folder-for-new-files 。我不明白的是为什么我的脚本在我使用pid x.

36285 pts/1    S+     0:00 /bin/bash ./observe2.sh /home/user1/testfolder
36286 pts/1    S+     0:00 inotifywait -m /home/user1/testfolder -e create -e moved_to
36287 pts/1    S+     0:00 /bin/bash ./observe2.sh /home/user1/testfolder
Run Code Online (Sandbox Code Playgroud)

为了更快地测试,我更改了链接脚本,以便您可以通过 $1 传递任何文件夹进行观察,并保存为observe2.sh

#!/bin/bash
inotifywait -m $1 -e create -e moved_to |
    while read path action file; do
        echo "The file '$file' appeared in directory '$path' via '$action'"
        # do something with the file
    done
Run Code Online (Sandbox Code Playgroud)

为什么脚本进程会出现两次?过程中是否有分叉?有人可以解释为什么会发生两个进程的这种行为吗?

Cha*_*ffy 5

因为它有管道。

管道派生出一个子外壳——第二个进程——并将它们连接起来。在foo | bar- 这些都是外部非外壳命令的情况下 - 子外壳exec是实际命令,因此丢失了它们的进程树条目。当该管道的元素被写入 shell 时,执行它的 shell 会保留在进程树中。


也就是说,我建议[1]写得有点不同:

while read -r path action file; do
    echo "The file '$file' appeared in directory '$path' via '$action'"
    # do something with the file
done < <(exec inotifywait -m "$1" -e create -e moved_to)
Run Code Online (Sandbox Code Playgroud)

这将使 while 循环保持在主进程中,exec并使分叉的子shell 将自身替换为inotifywait. 因此,您在while循环内所做的更改——例如在脚本中设置变量——即使在脚本从循环中移出后仍将持续存在,而如果您将循环放入管道中,则在其中设置的任何变量的范围都为子壳。有关详细信息,请参阅BashFAQ #24


[1] - 实际上,如果您想彻底涵盖所有极端情况,我建议您以更不同的方式编写它。由于 POSIX 文件名可以包含除 NUL 或//当然也可以存在于路径名中)之外的任何字符,因此空格或换行符分隔名称是一个非常糟糕的主意;围绕以空格结尾的名称的现有实现中也存在错误。不幸的是,由于inotifywait不允许自定义格式字符串包含\0以 NUL 分隔的字符串,因此从它获得完全明确的输出非常棘手;StackOverflow 上的其他答案已经涵盖了它,我不太愿意在这里复制它们的内容。