将 xargs 与 $() 一起使用 - 运算符优先级?

Ken*_*h L 3 linux bash xargs

受到这个问题的启发。简而言之,最初的问题是“如何将所有文件全部:2f转换-为文件夹内的所有文件”。例如,如果我有一个文件,./abc:2fdef它应该重命名为./abc-def.

起初我认为这是一个简单的任务......find所有文件然后sed替换:2f-. 在尝试构建单行时,我想出了以下命令:

find . -type f -name '*:2f*' | xargs -I {} mv {} $(echo {} | sed "s/:2f/-/ig")
Run Code Online (Sandbox Code Playgroud)

但是它不起作用。经过大量测试,我发现问题出在xargs部分 - $() 在用文件名xargs替换地方之前执行{}。详细说明,

xargs -I {} mv {} $(echo {} | sed "s/:2f/-/ig")
Run Code Online (Sandbox Code Playgroud)

被评估为($(...)被评估)

xargs -I {} mv {} {}
Run Code Online (Sandbox Code Playgroud)

进而

mv ./abc:2fdef ./abc:2fdef
Run Code Online (Sandbox Code Playgroud)

这不是我所期待的。所以,我的问题是,{}在评估$(...)零件之前,我可以让 xargs 全部替换为文件名吗?

slh*_*hck 5

不,因为xargs在 shell 调用之前不能xargs$(…)已经评估的东西替换某些东西。这是由于shell 扩展的顺序。

我可能会这样做:

find . -type f -name '*:2f*' -exec bash -c 'mv -- "$0" "${0//:2f/-}"' {} \;
Run Code Online (Sandbox Code Playgroud)

在这里,shell 调用是从 找到的每个文件中评估的find,并且字符串替换是使用 shell 功能完成的而不是对sed.

此外,这种方法更安全,因为文件名中的空格得到了正确处理(通过对mv调用的参数进行双引号)。更多关于这里


对于这些任务,您可能需要更高效的工具,例如zmvZsh 中提供的 ,或rename与许多 Linux 发行版捆绑在一起的 Perl :

rename 's/:2f/-/' *
Run Code Online (Sandbox Code Playgroud)