我正在尝试批量重命名以驼峰命名的文件,以包含相邻大小写字母之间的空格。我使用的是 Mac 操作系统,因此我使用的实用程序是 BSD 变体。
例如:
250 - ErosPhiliaAgape.mp3 => 250 - Eros Philia Agape.mp3
我正在尝试使用以下命令将find
相关文件通过管道传输到在子 shell 中mv
运行的文件:sed
find . -name "*[a-z][A-Z]*" -print0 | xargs -0 -I {} mv {} "$(sed -E 's/([a-z])([A-Z])/\1 \2/g')"
单独来看,这些命令工作得很好:find
提取正确的文件并sed
正确重命名它,但是当我将它们与 xargs 结合使用时,什么也没有发生。
我需要改变什么才能使这项工作正常进行?
答案1
问题是$(...)
命令中的子 shell 是在运行命令时评估的,而不是xargs
针对每个文件评估的。您可以使用find
's-exec
来计算 a 中每个文件的命令sh
,并适当地替换引用:
find . -name "*[a-z][A-Z]*" -type f -exec sh -c 'echo mv -v "{}" "$(echo "{}" | sed -E "s/([a-z])([A-Z])/\1 \2/g")"' \;
如果这个输出看起来不错,请删除echo
in echo mv
。请注意,由于echo | sed
,这不适用于嵌入\n
. (但这是您自己的尝试中已经存在的限制,所以我希望这是可以接受的。)
答案2
您的sed
命令正在从管道获取输入。它与 竞争xargs
,因此两个命令中的每一个都会获取由 写入的部分数据find
(每个字节仅发送到一个读取器),并且无法预测哪个命令接收什么。
您需要将每个文件名通过管道传输到 中sed
,这意味着您需要一个中间 shell 来设置管道。
find . -name "*[a-z][A-Z]*" -print0 | xargs -0 -I {} sh -c 'mv "$0" "$(echo "$0" | sed -E '\''s/([a-z])([A-Z])/\1 \2/g'\'')"' {}
你不需要xargs
,它基本上没有用。find
可以直接调用程序。
find . -name "*[a-z][A-Z]*" -exec sh -c 'mv "$0" "$(echo "$0" | sed -E '\''s/([a-z])([A-Z])/\1 \2/g'\'')"' {} \;
或者,安装任何 Perlrename
实现,例如File::Rename
或来自Unicode::Tussle
。
cpan File::Rename
find -depth . -exec rename 's!([a-z])([A-Z])(?!.*/)!$1 $2!g' {} +
该(?!.*/)
位可防止在目录名称中触发替换(如果目录名称包含驼峰命名法)。传递-depth
到find
可确保如果重命名目录,则在find
遍历该目录后会发生这种情况。
答案3
如果您更喜欢使用 GNU Parallel,解决方案是:
find . -name "*[a-z][A-Z]*" -type f | parallel mv -v {} '{= s:([a-z])([A-Z])(?!.*/):$1 $2:g =}'