如何在 find -exec 中使用命令替换?

如何在 find -exec 中使用命令替换?

如果我只使用basename {} .txt,它会起作用:

find . -iname "*.txt" -exec basename {} .txt \;

它只会打印xxx而不是./xxx.txt.如果我$(basename {} .txt)-exec选项中使用,它将失败:

find . -iname "*.txt" -exec echo "$(basename {} .txt)" \;

它只会打印./xxx.txt

我怎么解决这个问题?我希望我可以用作$(basename {} .txt)其他cmd的参数。我必须使用 xargs进行操作sh -c或管道吗?-exec basename {} \;

答案1

尝试:

find -iname "*.txt" -exec sh -c 'for f do basename -- "$f" .txt;done' sh {} +

你的第一个命令失败了,因为$(...)在子 shell 中运行,它被{}视为文字。所以basename {} .txt返回{},你find就变成了:

find . -iname "*.txt" -exec echo {} \;

哪个打印文件名匹配。

答案2

$()如果使用sh -cand 单引号,您仍然可以用于命令替换:

find . -iname "*.txt" -exec sh -c 'echo "$(basename {} .txt)"' \;

单引号阻止主 shell 执行其中的子命令$(),因此可以通过sh -c find命令已替换{}为文件名。看Stack Overflow 上的这个答案以获得更彻底的解释。

请注意,您还可以在 内添加双引号$()来处理文件名中的空格:

find . -iname "*.txt" -exec sh -c 'echo "$(basename "{}" .txt)"' \;

答案3

相反,还有另一种使用 bash 管道和xargs命令的方法:

    find . -iname '*.txt' | xargs -L1 -I{} basename "{}"

xargs上面的命令中:

参数-L1表示逐行执行命令行的输出find

-I{}参数旨在find用双引号括起命令的输出,以避免 bash 分词(当文件名包含空格时)。

答案4

同意@an9wer,因为管道更容易(输入、记住、阅读等),尽管我会尝试遵守 posix xargs 并使用 nil 字符作为分隔符;例如,alpine 上的 xargs 没有标记-L1,这意味着您必须在 和find命令中显式标记 nil xargs如果您正在谈论运行临时命令,请忽略这一点,但如果这是一个在不是您的生产环境的东西上开发的脚本,并且假设您的生产环境永远不会改变,那么这真的是一件大事。

与 @an9swer 相同,但使用 nil 字符作为分隔符,使用-n标志一次提供一个结果作为 的参数basename。我们也不需要模板标志-I,因为它是推断的并且是默认行为。

find . -iname '*.txt' -print0 | xargs -0 -n1 -- basename

上述的另一个优点是 xargs 允许并行执行传递的语句,这意味着您可以并行运行上述语句,最多可达您拥有的核心数量,以馈送到某些下游进程。如果您正在处理数量接近无穷大的文件,或者工作流程优化很重要,那么这是一个巨大的、唾手可得的胜利

是的,我有点炫耀,但没关系,因为我可以(有时)表现得小气。

相关内容