如果我只使用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 -c
and 单引号,您仍然可以用于命令替换:
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 允许并行执行传递的语句,这意味着您可以并行运行上述语句,最多可达您拥有的核心数量,以馈送到某些下游进程。如果您正在处理数量接近无穷大的文件,或者工作流程优化很重要,那么这是一个巨大的、唾手可得的胜利
是的,我有点炫耀,但没关系,因为我可以(有时)表现得小气。