【问题标题】:bash shell: change multiple file names to add leading zerosbash shell:更改多个文件名以添加前导零
【发布时间】:2014-07-12 04:22:32
【问题描述】:

我有几个具有这种模式的文件:prefix.1.*prefix.2.*prefix.3.* 等...我希望将它们的名称分别更改为prefix.01.*prefix.02.*prefix.03.*等。这样,它们将按名称正确排序,因为文件集中已经有两位数的文件名(例如prefix.27.*)。

如何使用 bash shell 中可用的命令来做到这一点?


注意:就在这个问题之后,我不得不处理一个文件列表,如prefix.1.*prefix.2.*prefix.17.*prefix.157.*(任意,不按顺序编号),目的是转换为@ 987654333@、prefix.002.*prefix.017.*prefix.157.*,使用插入正确数量的前导零的通用方法。如果您遇到这种情况,我强烈建议您遵循下面mklement0 answer 提供的通用解决方案(更通用的重命名解决方案)。

【问题讨论】:

    标签: bash shell


    【解决方案1】:
    for file in prefix.[0-9].*
    do
       mv "$file" "${file/prefix./prefix.0}"
    done
    

    【讨论】:

    • 很好,但您可能指的是"${files[@]}" 而不是files;此外,没有理由使用extglob(顺便说一句:您可能的意思是shopt -s extglob - 只是shopt extglob 只会报告选项的当前状态);直接使用for file in prefix.[0-9].* 就足够了。一般来说(在这种情况下不需要),你应该双引号你的变量 [expansion] 引用;即:"$file""${file/prefix./prefix.0}"。最后——只要你使用数组——如果你的 glob 中的 * 扩展到带有嵌入空格的东西,你的解决方案就会中断。
    • 感谢更新;我一直在更新我的评论,所以你可能还没有看到最终版本:in files 部分仍然不起作用;如果你用in prefix.[0-9]* 替换它(并放弃files 数组),所有问题都会消失 - 即,即使是嵌入空格的文件名也将得到正确处理。
    • @mklement0 OP 提出问题的方式,我认为文件名中不会有任何空格。不过,有效点。
    • 点了,但它让很开心。 :); +1。开个玩笑,我觉得从通用、稳健的答案中受益匪浅——如果在这些方面改进答案的成本相当低——就像在这种情况下——我觉得值得这样做。 (另一种方法是指出限制。)
    • @khyox:我的荣幸;如果您发现自己需要更频繁地执行此类重命名任务,我建议您查看基于 Perl 的 rename 实用程序,它非常灵活。我已经添加了另一个基于它的答案,它可以满足您在评论中描述的内容:stackoverflow.com/a/23940342/45375
    【解决方案2】:

    更通用的重命名解决方案,由 OP 后来的 cmets 提示:

    使用基于 Perl 的 rename 实用程序,它是 一些 Linux 发行版(例如 Ubuntu)的标准配置。 警告:某些发行版具有与util-linux 软件包同名的不同实用程序。
    在 OS X 上,您可以通过 Homebrew 使用 brew install rename 进行安装。

    两种解决方案都动态地确定所需的填充宽度。


    更简单的解决方案,如果文件已经按顺序编号并且只有填充必须执行[不符合 OP 的要求 - 请参阅下面的其他解决方案]:

    printf '%s\n' prefix.[0-9]*.* |                # enumerate files of interest
      sort -t. -n -k2,2 |                          # sort them by the embedded number
        rename -n -N '...01' -e 's/\.\d+\./.$N./'  # rename with appropriate padding
    
    • -n 只进行试运行,只显示会发生什么;删除它以执行实际重命名
    • -N '...01' 指定特殊计数器变量 $N 的格式,根据输入文件的数量自动确定适当的零填充
    • 's/\.\d+\./.$N./' 是一个 Perl 表达式,它用适当填充的计数器变量 $N 替换现有的数字组件。

    更复杂的解决方案,保留现有数字并仅填充它们。

    在这种情况下,填充宽度来自文件名中嵌入的最大现有数字

    # Determine the longest (largest) number contained in the matching filenames...
    maxNum=$(printf '%s\n' prefix.[0-9]*.* | sort -nrt. -k2,2 | head -1 | cut -d. -f2)
    # ... and derive the padding length from it.
    padLength=${#maxNum}
    
    # Perform renaming by zero-padding the *existing* numbers in the filenames.
    rename -n -e 's/\.(\d+)\./sprintf(".%0'${padLength}'s.", $1)/e' prefix.[0-9]*.*
    
    • -n 仅进行试运行,仅显示会发生的情况;删除它以执行实际重命名
    • 注意在s/.../.../ 命令中使用Perl 的e 标志,它允许在替换字符串中使用任意Perl 表达式;在这种情况下,sprintf 表达式用于对输入数字进行零填充;这种方法的功劳属于@Adrian Frühwirth 的回答here

    一般来说,使用shopt -s extglobprefix.+([0-9]).* 可以使匹配文件名更加健壮;同样,shopt -s nullglob 可以更轻松地确定 任何 个文件是否匹配。

    【讨论】:

    • 令人印象深刻!再次非常感谢您对完整性的承诺!
    • 谢谢,@khyox。我很高兴你发现它很有用。
    • 我在我的问题中添加了一条注释,以便在适当的情况下指向这个答案。
    【解决方案3】:

    注意一个纯粹的bash 解决方案,但更简洁一点(*):

    printf '%s\0' prefix.[0-9].* | 
      xargs -0 -I % bash -c 'f="%"; mv "$f" "${f/\./.0}"'
    

    这假设 glob prefix.[0-9].* 扩展为至少 1 个实际文件名。
    如果 NO 文件可能匹配,请先执行 shopt -s nullglob(如果需要,稍后恢复该 shell 选项的值)。


    性能说明

    虽然这比@R Sahu's solution简洁,但它会性能更差,因为会为每个输入文件名生成一个额外的子 shell (bash -c) - 所以以便能够将文件名分配给变量并对其使用扩展。


    (*) 就字符数而言,@R Sahu's solution 实际上更短,但缺少 循环(以及更少的行)可能会导致此答案被感知 更短。归根结底,这个解决方案本身并没有增加太多 - 可能只是作为使用 xargs 的高级示例:

    • printf '%s\0' 确保所有匹配的文件名都使用 NUL 字符通过管道传递。作为分隔符。
    • xargs -0 然后确保文件名被单独识别,即使它们包含嵌入的空格。
    • -I % 确保 每个 输入文件名导致其自己调用随后的命令,% 实例替换为手头的文件名。
    • bash -c 然后调用一个子 shell(一个恰好是另一个 bash 实例的子进程)并计算指定的字符串。

    【讨论】:

      【解决方案4】:

      我发现的最简单的方法就是在 bash shell 中输入这一行:

      for ITER in 1 2 3 4 5 6 7 8 9; do for FILE in prefix."$ITER".*; do mv "$FILE" $(echo "$FILE" | sed 's/prefix.'$ITER'/prefix.0'$ITER'/'); done; done
      

      【讨论】:

        【解决方案5】:

        另一种方式>>

        #!/bin/bash
        count=1
        for i in $(ls prefix*)
        do
        Newname=prefix\.$(printf "%02d" $count)\.txt
        mv $i $Newname
        count=$(($count + 1))
        done
        

        【讨论】:

        • 有希望,但最好直接使用路径名扩展(don't parse ls output);为了安全起见,您还应该双引号引用您的变量引用(以便正确处理带有嵌入空格的文件名)。
        【解决方案6】:

        到目前为止,答案很好。如果所有文件的扩展名相同。只需使用以下。这里的扩展名是.jpg 您可以将 prefix_ 更改为您选择的前缀。 此外,如果您想要更多前导零,只需将 %02 更改为 %03 等等。

        rename -n -e 's/\d+/sprintf("prefix_%02d",$&)/e' -- *.jpg
        

        【讨论】:

          猜你喜欢
          • 2012-02-13
          • 1970-01-01
          • 2017-04-07
          • 1970-01-01
          • 2022-06-27
          • 2012-10-26
          • 1970-01-01
          • 2012-05-01
          相关资源
          最近更新 更多