使用反引号或$()与xargs、sed或awk。

5
假设我想要通过管道将 find 的输出传递给 xargs(在 bash 中),并且我希望更改一些以 jpg.jpg 结尾的文件名,使其仅以 .jpg 结尾:
使用 sed
find . -iname '*jpg.jpg' | xargs -I % mv -iv % $(echo % | sed 's/jpg.jpg/.jpg/g')

然而,这并不会在 mv 的目标文件中将 jpg.jpg 替换为 .jpg。通过使用 awk:
find . -iname '*jpg.jpg' | xargs -I % mv -iv % $(echo % | awk '{gsub(/jpg.jpg/,".jpg")}; 1')

这并没有进行任何替换。我有遗漏什么吗?
5个回答

5
我会编写一个脚本来接受文件名并进行重命名:
#!/bin/sh

for FILE
do
    mv $FILE `basename $FILE jpg.jpg`.jpg
done

那个脚本可以通过xargs轻松调用。

如果你想将它压缩成一条命令行,虽然可以这样做,但通常不值得麻烦。

编辑:如果我必须在一条命令行上执行它,我可能会使用另一个技巧而不需要xargs:使用sedfind的输出转换为shell脚本:

find . -iname '*jpg.jpg' | sed -e 's/\(.*\)jpg\.jpg$/mv & \1.jpg/' | sh
< p > xargs 能够少派生子进程的优势被需要反复运行mv的需求所抵消。如果你真的需要那个优势,你需要一个像我第一个选项一样的解决方案,但是用类似perl的东西编码,它可以执行多个rename()调用而不会派生任何mv子进程。


通常是这样的,但我觉得如果xargs无论如何都会在空格处分割文件名,那么强调这些细节会让人感到困惑。如果你采纳Ed的建议,也要使用... -print0 | xargs -0 ... - Ben Jackson

4
如果您的系统上有“rename”命令,则以下命令应该可以工作:
rename 's/(.*)jpg.jpg/$1.jpg/g' *jpg.jpg

3
$(...) 在运行 xargs 前被 bash 计算,因此 xargs 只是将该表达式的输出作为第二个参数传递给 mv 命令。因此,它等效于:
... | xargs -I % mv % %

1
有什么正确的方法可以解决这个问题(在原始字符串上运行sed,但是将整个sed操作嵌入使用原始未被sed处理过的字符串的命令的中间位置)?请参阅我的相关问题:https://dev59.com/BHzaa4cB1Zd3GeqPNT_U - Excalibur

2
我建议您使用GNU Parallel来处理这个问题:
find . -iname '*jpg.jpg' | parallel mv {} {.}

{}会被输入替换,{.}会被输入减去最后一个扩展名替换。使用--dry-run和parallel一起使用可以查看将要执行的操作。

编辑 - 应该处理file1jpg.jpg

根据您对大小写的需求,可以使用参数扩展或GNU sed来实现:

参数扩展:

find . -iname '*jpg.jpg' | parallel v={}\; mv '{}' '${v/jpg.jpg/.jpg}'    

使用GNU sed:

find . -iname '*jpg.jpg' | parallel mv '{}' '$(echo {} | sed -r "s/jpg\\.(jpg)$/.\1/I")'

如果我有一个名为 file1jpg.jpg 的文件,find . -iname '*jpg.jpg' | parallel mv {} {.} 命令会将 file1jpg.jpg 重命名为 file1jpg。我想要的是将 file1jpg.jpg 重命名为 file1.jpg,而不是 file1jpg - nrz
@nrz:仍然可以使用并行方式完成,只是变得更加复杂。请参见编辑。 - Thor
使用参数扩展的命令可以正常工作,但是使用GNU sed的命令失败了,显示如下错误:mv: missing destination file operand after ./file2jpg.jpg' Try mv --help' for more information. - nrz
@nrz:parallel 尝试运行哪些命令?您可以使用 --dry-run 列出它们。 - Thor
@nrz:我在使用zsh进行测试,其中<<<没有引起任何问题,但似乎需要使用echo。现在试试看。 - Thor
显示剩余2条评论

1

不需要使用 xargsfind

for i in *jpg.jpg; do mv "$i" "$(echo $i | sed 's/jpg.jpg$/.jpg/')"; done

而且也没有真正需要使用 sedawk

for i in $(find . -type f -name "*jpg.jpg"); do mv "$i" "${i/%jpg.jpg/.jpg}"; done

这里还有另一种方法:

find . -type f -name "*jpg.jpg" -exec bash -c 'mv $0 ${0/%jpg.jpg/.jpg}' {} \;

第一行正常工作,第二行出现错误“mv: 无法将 `./myfilejpg.jpg' 移动到 `./myfile.jpg/': 不是目录。” 这可以通过将 "${i/%jpg.jpg/.jpg/}" 更改为 "${i/%jpg.jpg/.jpg}" 来修复。第三行似乎也需要类似的修复。 - nrz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接