你可以在“查找”过程中修改文件名吗

Mau*_*itz 2 find

find用于{}指示“此文件”(ish)。您可以将一系列文件输入到 中myprog,因此:

find ./tests/ -name *.in -exec myprog -i {} \;
Run Code Online (Sandbox Code Playgroud)

有没有办法修改里面的名字{}?就我而言,我用来-i定义输入文件和-o输出,并且我希望输出稍微修改一下文件名,这样“a.in”将生成“a.out”。理想情况下,我想要达到以下效果:

find ./tests/ -name *.in -exec myprog -i {} -o {}.out \;
Run Code Online (Sandbox Code Playgroud)

此外,输出目录可能是不同的路径。在这种情况下,输出可能不会到达,/tests/但可能会到达/tests_20220615/

我已经查看了许多带有 示例的页面find,但没有出现类似的情况,所以也许“不”?

我知道有一些方法可以在 bash 或 zsh 中使用循环来做到这一点,但是可能的陷阱列表很棒(“nullglob”?!),如果find可以做到这一点,那么对于这个菜鸟来说似乎更安全。

Sté*_*las 7

find不会让您修改文件的路径,某些find实现不会让您{}与其他内容连接,有些甚至不支持{}多次传递,但您始终可以运行一些命令,例如可以进行转换的 shell:

find ./tests/ -name '*.in' -type f -exec sh -c '
  ret=0
  for file do
    myprog -i "$file" -o "${file%.*}.out" || ret="$?"
  done
  exit "$ret"' sh {} +
Run Code Online (Sandbox Code Playgroud)

myprog在上面,我们不是直接执行,而是执行sh并向其传递一些内联代码以及找到的文件的路径(而{} +不是{} ';'传递尽可能多的文件)。

sh依次循环这些文件,并myprog在对它们应用转换后调用,例如${file%.*}删除扩展名。

请注意 周围的引号*.in。如果没有它们,您运行该命令的 shellfind会尝试将其扩展到当前目录中名称以 结尾的文件列表,.in而不是将该模式字面传递给find.

上面,我们告诉sh如果任何调用失败,则以失败退出状态退出myprog。该失败将反映在 的退出状态中find,因此您可以根据需要采取操作,或者在errexit启用该选项的情况下退出脚本。但不可能在第一次失败时中止myprog

如果使用zshshell,您还可以在内部进行查找:

set -o errexit
for file (./tests/**/*.in(ND.)) myprog -i $file -o $file:r.out
Run Code Online (Sandbox Code Playgroud)

将在第一次失败时退出,并且还会按词法顺序处理列表(您始终可以添加oNglob 限定符来禁用该排序)。

另一种方法是find打印文件,将其通过管道传输到执行转换的某个命令,然后通过管道传输到xargs. 例如:

find ./tests/ -name '*.in' -type f -print0 |
  gawk -v RS='\0' -v ORS='\0' -v OFS='\0' '
    {
      filein = fileout = $0; sub(/\.in$/, ".out", fileout)
      print "-i", filein, "-o", fileout
    }' | xargs -r0 -n4 myprog
Run Code Online (Sandbox Code Playgroud)

如果任何调用失败,将再次xargs返回非零退出状态myprog。GNUxargs可以与其-P选项并行运行多个调用。

或者你可以perl对其进行后处理并让它运行:

find ./tests/ -name '*.in' -type f -print0 |
  perl -l -0ne '
    system("myprog", "-i", $_, "-o", s/\.in\Z/.out/r) == 0 or
      $ret = 1;
    END {exit $ret}'
Run Code Online (Sandbox Code Playgroud)

请注意,后处理 的输出的方法find将掩盖其失败退出状态(如果有)(例如无法进入某些目录时),除非您设置了pipefailshell(如果支持)。

使用管道还会影响myprog标准输入的内容(例如,如果需要提示用户)。GNU在、其他一些xargs上打开标准输入,该方法将保持原样,这意味着它将是来自/ 的管道。/dev/nullperlfindgawk

  • @MauryMarkowitz,你总是需要担心引用:-),一旦你修复了 `*.in` 周围丢失的引号,就不再涉及通配符,因此 `nullglob` 不能产生任何效果。 (2认同)