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
可以做到这一点,那么对于这个菜鸟来说似乎更安全。
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
。
如果使用zsh
shell,您还可以在内部进行查找:
set -o errexit
for file (./tests/**/*.in(ND.)) myprog -i $file -o $file:r.out
Run Code Online (Sandbox Code Playgroud)
将在第一次失败时退出,并且还会按词法顺序处理列表(您始终可以添加oN
glob 限定符来禁用该排序)。
另一种方法是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
将掩盖其失败退出状态(如果有)(例如无法进入某些目录时),除非您设置了pipefail
shell(如果支持)。
使用管道还会影响myprog
标准输入的内容(例如,如果需要提示用户)。GNU在、其他一些xargs
上打开标准输入,该方法将保持原样,这意味着它将是来自/ 的管道。/dev/null
perl
find
gawk