测试文件存在时出乎意料的结果



让我们假设我们有一个空文件夹,在其中创建以下bash脚本:

#!/bin/bash
while true; do
    inotifywait -r -e modify,move,create,delete $(dirname $0)
    if [ -f 'asdf.txt' ]; then
        echo "YES!"
    else
        echo "NO!"
    fi
    # Do something that takes some time...
    sleep 0.1
done

基本上,它等待该目录中的文件进行修改/移动/创建/删除,然后,如果文件asdf.txt存在,则将YES!打印为输出。

现在让我们创建该asdf.txt文件并运行BASH脚本。在运行脚本的情况下,如果我用VIM编辑asdf.txt并为其编写更改,那么我可以看到inotifywait检测到的CREATE事件,但我唯一获得的输出是NO!。这是为什么?我尝试过的其他编辑不会发生这种情况。

请注意,如果我在inotifywait和文件测试之间添加一个小延迟,则可以正常工作:

[...]
    inotifywait ...
    sleep 0.01
    if [-f 'asdf.txt' ]; ...
[...]

在文件刚刚使用VIM进行编辑后而无需在脚本中添加任意延迟之后,是否可以测试该文件?

更新

这与VIM的交换文件有关。实际上,如果您编辑vimrc并告诉VIM避免创建交换文件,这将不会发生。

但是,此脚本将由最终用户运行,我不能期望他们具有任何特定的VIM配置(也不要求它们避免交换文件或更改交换文件默认目录)。

我怀疑应该使用inotifywait中的--exclude选项有一种方法,但是我尚未成功使用它(使用--exclude ".*.swp"尝试)。

,而不是将inotifywait放入循环中,使用监视选项并将输出输送到循环中,例如:

while IFS= read -r -u 5 EVENT
do
    (PARSE EVENT)
    if [ (I am interested in that file) ]; then
        echo "YES!"
    else
        echo "NO!"
    fi
    # Do something that takes some time...
    # Do not sleep
done 5< <(inotifywait -m ....)

上面的代码使用文件描述符5来保护标准输入,以防您的"某物"需要用户输入。

一个优点是,您将收到在发生"需要一些时间"的事件时发生的事件。如果要避免接收事件的延迟,您可能需要在后台执行"某物"(使用&)。

vim实际上在保存到文件时会做很多事情:

./ CREATE 4913
./ DELETE 4913
./ MOVED_FROM asdf.txt
./ MOVED_TO asdf.txt~
./ CREATE asdf.txt
./ MODIFY asdf.txt
./ DELETE asdf.txt~
./ MODIFY .asdf.txt.swp
[...]

实际上可能会期望有些(例如创建和编辑交换和备份文件)。但是有些可能有点违反直觉:

  • 它首先创建并破坏测试文件4913,以尝试验证它可以在您看到文件的目录中创建文件并设置UID/GID。
  • 它实际上 moves 将原始文件移至备份文件(这就是测试可能会失败的原因),然后创建一个新文件来替换原始文件。

在选择事件inotifywait会触发时,创建适当的规则并更加限制,我们可以使用:

inotifywait --exclude ".*.(swp|swx)|4913|.*~" -r -e modify,moved_to,create,delete $(dirname $0)

请注意,我们失去了moved_from事件的跟踪。

这可以与弗雷德(Fred)的答案相结合,以将VIM编辑视为特殊情况(即:当我们首次看到4913文件时)。

最新更新