在“for f in *”sh 循环中间添加/删除文件时会发生什么?

在“for f in *”sh 循环中间添加/删除文件时会发生什么?

我在网上找到了一个for循环的例子。现在我想在我的代码中使用它,但我不确定这个循环是如何运行的

for entry in "$search_dir"/* 
do
  echo "$entry"
done

现在我想问一下

  1. 它是否在每次迭代中查找 search_dir 并将 search_dir 中的文件复制到每次迭代中的条目变量一个文件?
  2. 或者我拍摄 search_dir 的所有内容的快照,然后将该快照存储到条目变量中?
  3. 如果有人在循环仍在工作时在 search_dir 中插入一些文件,输出是否会发生变化?

答案1

当 shell 到达for- 语句时,它将扩展 的值$search_dir并执行文件名通配以生成将迭代的目录条目列表。这种情况只会发生一次,如果循环执行时 中的内容$search_dir消失或者有新文件/目录添加到该目录中,则这些更改将不会被拾取。

如果循环对名称为 的目录条目进行操作$entry,则可能需要测试它们是否存在于循环中,特别是如果已知循环需要很长时间才能运行,并且有大量文件在不断变化,出于这样或那样的原因:

for entry in "$search_dir"/*; do
    if [ -e "$entry" ]; then
        # operate on "$entry"
    else
        # handle the case that "$entry" went away
    fi
done

正如 Stéphane 在评论中正确指出的那样,这是一个多余的测试最多案例。

答案2

shell 在开始运行循环体之前完全确定要循环的值列表。那是:

  1. shell 使用变量的值构建路径search_dir
  2. shell 收集指定目录中的文件名列表,以构建通配符模式的匹配列表。
  3. shell 依次对匹配列表中的每个元素执行循环体。

search_dir您可以在循环运行时更改变量 的值,并更改目录的内容。这不会影响循环作用于哪些文件。

如果在循环处理其他文件时删除一个文件,那么一旦到达该文件,该文件将不存在。根据您在循环中执行的操作,这可能重要也可能不重要。如果存在可能删除文件的并发进程,请注意,在处理之前测试文件是否存在并不能真正解决该问题,因为该文件可能会在您测试和开始处理之间被删除

如果您需要将文件标记为已处理以确保不会处理它两次,那么此脚本应在处理完文件后将其移动到另一个目录。将文件移动到另一个目录(在同一文件系统上)是原子:要么尚未完成,要么已经完成,没有中间状态。但再一次,如果不同的进程(可能是该脚本的另一个实例)移动文件,那么循环有时会命中循环执行时移动的文件。

如果您想在创建新文件时对其进行处理,则需要再次循环。显然,文件可以在循环执行期间创建,或者在处理完所有先前的文件之后创建,因此脚本需要永远运行。有一些工具可以等待目录中创建文件。在 Linux 上,基本功能是inotify;如果您需要在创建文件时对其进行处理,那么inotifywait或者因克朗应该对你有帮助。请记住,inotify 仅通知您创建的文件(或根据您的触发器修改或访问)基于 inotify 的命令启动;您还需要处理先前存在的文件,但您不能仅仅这样做,for entry in *; do …; done; inotifywait …因为可以在循环执行期间甚至在命令inotifywait启动时创建文件。

相关内容