我正在使用 FreeBSD,并且在执行包含 {} 作为字符串一部分的命令时遇到困难。例如,如果重命名找到的文件。请注意,这是情况的一个示例。问题本身是关于解决“{}”语法的一般问题:
find . -type f -name 'data*' -execdir mv {} OLD_{} \;
find . -type f -name 'data*' -execdir mv {} archive/{} \;
请注意,-execdir
通过从包含目录中执行命令并扩展{}
为仅文件名,可以避免包含目录路径的问题。
有两个问题:
- 如何正确引用
-execdir mv
子句中的参数(许多文件的名称中都会有空格或单引号)。 - 如何让目标完全替换文件名。
出现第二个问题是因为{}
“找到的项目的路径”似乎只有在被前导/尾随空格包围时才会扩展,这会弄乱命令参数。具有非空格前导和尾随字符的示例:
# /usr/bin/find . -maxdepth 1 -execdir echo "(result):" {} \;
(result): .
(result): dir 1
(result): dir 2
(result): dir 3
# /usr/bin/find . -maxdepth 1 -execdir echo "(result):"{} \;
(result):
(result):
(result):
(result):
# /usr/bin/find . -maxdepth 1 -execdir echo {}":(result)" \;
:(result)
:(result)
:(result)
:(result)
man find
指出" -exec 和 -ok 初级的历史实现不会替换实用程序名称或实用程序参数中的字符串“{}”(如果它前面或后面有非空白字符)。无论实用程序名称中的何处,此版本都会替换它或出现的争论”,但这似乎并没有发生。
如何执行我想要运行的命令?
答案1
当用作shellfind
时,在 FreeBSD 11.1-RELEASE 中无法重现此行为。/bin/sh
我曾是能够在两者下重现它/bin/csh
,尽管/bin/tcsh
。
csh
要在和下更正此问题tcsh
,请引用{}
as\{\}
或 as '{}'
,或使用以下方法。
要在将当前路径名用作字符串的一部分时find
无法正确扩展的实现中正确地将当前路径名与其他字符串连接起来,可以使用子 shell 来完成此操作。{}
例子:
find . -type f -name '*.c' -exec sh -c 'printf "(result):%s\n" "$@"' sh {} +
或者,与echo
(但请参阅“为什么 printf 比 echo 更好?”),
find . -type f -name '*.c' -exec sh -c '
for name do
echo "(result):$name"
done' sh {} +
或者
find . -type f -name '*.c' -execdir sh -c '
for name do
mv -- "$name" "OLD_${name##*/}"
done' sh {} +
也就是说,为子 shell(sh -c
此处)提供路径名作为命令行参数,然后在生成的 shell 中使用这些路径名来连接它们,就像通常使用 shell 变量一样。
(${name##*/}
以上只是为了防止 GNU在使用时在路径名find
前面添加)./
-execdir
有关的:
答案2
我对最后两个例子感到困惑。在这里,如果find
不替换{}
,那么人们会认为它会保持原样,输出将是(result):{}
:
# /usr/bin/find . -maxdepth 1 -execdir echo "(result):"{} \;
(result):
也许是你的外壳破坏了这些foo{}
字符串?
我tcsh
似乎就是这么做的:
$ tcsh -c 'echo foo{}bar '
foobar
$ tcsh -c 'echo foo {} bar '
foo {} bar
这是还提到在关于引用的另一个问题的评论中{}
,GNU 查找并屏蔽某些 shell 的 {} - 哪个?。
解决方法:在包含以下内容的字符串两边加上引号{}
:
$ tcsh -c 'echo "foo{}bar" '
foo{}bar
(另一个需要引用的 shell{}
是fish
,但它也会删除单独的{}
。)
答案3
您最好使用类似的东西:
find . -type f -name 'data*' -print0 | xargs -0 -I % mv '%' 'OLD_%'
通过使用 find -print0 | xargs -0,每个文件名由空字节分隔,因此文件名中的空格和其他特殊字符不会导致文件名被拆分为两个单独的参数。