【问题标题】:Using backticks or $() with xargs and sed or awk将反引号或 $() 与 xargs 和 sed 或 awk 一起使用
【发布时间】:2012-10-21 11:49:17
【问题描述】:

假设我想将一些以 jpg.jpg 结尾的文件名更改为仅以 .jpg 结尾(在 bash 中),并且我想通过管道将 find 的输出传送到 xargs 来做到这一点:

通过使用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')

这也没有任何替代。我错过了什么吗?

【问题讨论】:

    标签: bash sed awk xargs


    【解决方案1】:

    如果您的系统上有rename 命令可用,那么以下命令应该可以工作:

    rename 's/(.*)jpg.jpg/$1.jpg/g' *jpg.jpg
    

    【讨论】:

      【解决方案2】:

      我会为此使用 GNU 并行:

      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.jpgfind . -iname '*jpg.jpg' | parallel mv {} {.}file1jpg.jpg 重命名为file1jpg。我想要的是将file1jpg.jpg 重命名为file1.jpg,而不是file1jpg
      • @nrz:它仍然可以并行完成,只是变得更加复杂。见编辑。
      • 使用参数扩展的命令有效,但使用 GNU sed 的命令失败:mv: missing destination file operand after ./file2jpg.jpg'`Try mv --help' 了解更多信息。`
      • @nrz: 哪些命令是并行尝试运行的?您可以使用--dry-run 列出它们。
      • 如果有文件file1jpg.jpgfind . -iname '*jpg.jpg' | parallel --dry-run mv '{}' '$(<<< {} | sed -r "s/jpg\\.(jpg)$/.\1/I")' 打印如下:mv ./file1jpg.jpg $(<<< ./file1jpg.jpg | sed -r "s/jpg\\.(jpg)$/.\1/I")
      【解决方案3】:

      不需要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: cannot move `./myfilejpg.jpg' to `./myfile.jpg/': Not a directory. 这可以通过将"${i/%jpg.jpg/.jpg/}" 更改为"${i/%jpg.jpg/.jpg}" 来解决。第三行似乎可以进行类似的修复。
      【解决方案4】:

      我会通过编写一个接受文件名并进行重命名的脚本来做到这一点:

      #!/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
      

      xargs 能够分出更少的孩子的优势被mv 反复运行的需要所抵消。如果你真的需要那个优势,你需要一个像我的第一个选项一样的解决方案,但编码为perl 之类的东西,它可以执行多个rename() 调用而无需分叉任何mv

      【讨论】:

      • @EdMorton:通常是正确的,但是当xargs 无论如何都要在空格处分割文件名时,我觉得详述这些细节会令人困惑。如果您接受 Ed 的建议,也请使用 ... -print0 | xargs -0 ...
      【解决方案5】:

      $(...) 在运行 xargs 之前由 bash 求值,因此 xargs 只会看到该表达式的输出,因为它当前是 mv 的第二个参数。

      所以相当于:

      ... | xargs -I % mv % %
      

      【讨论】:

      • 解决这个问题的正确方法是什么(在原始字符串上运行 sed,但将整个 sed 操作嵌入到使用原始、未处理字符串的命令中间?请参阅我的相关问题:stackoverflow.com/questions/21636184/…
      猜你喜欢
      • 2014-03-24
      • 1970-01-01
      • 2014-03-11
      • 1970-01-01
      • 2016-03-10
      • 2017-04-23
      • 1970-01-01
      • 1970-01-01
      • 2013-02-24
      相关资源
      最近更新 更多