【问题标题】:Illogical number priority in file names in BASH [duplicate]BASH中文件名中的不合逻辑数字优先级[重复]
【发布时间】:2021-08-20 14:19:39
【问题描述】:

碰巧我在 BASH 中编写了一个脚本,其中一部分应该按数字顺序从指定目录中获取文件。显然,该目录中的文件命名如下:1、2、3、4、5 等。问题是,我发现在运行该目录中包含 10 个文件的脚本时,发生了一些对我来说非常不合逻辑的事情,因为脚本以奇怪的顺序获取文件:10、1、2、3 等。

如何让它从文件名的最小值到十进制的最大值?

另外,我正在使用以下代码行来定义循环和路径:

for file in /dir/*

不知道这是否重要,但我使用 Fedora 33 作为操作系统。

【问题讨论】:

    标签: linux bash loops terminal directory


    【解决方案1】:

    shell 根据每个单独的字符 的语言环境顺序(不一定是字节值)对名称进行排序。以1 开头的所有内容都将排在以2 开头的所有内容之前,以此类推。

    有两种主要方法可以解决您的问题:

    • sort -n(数字排序)文件列表,并对其进行迭代。
    • 重命名或重新创建目标文件(如果可以的话),因此所有数字的长度相同(以字节/字符为单位)。左边用0填充较短的数字(例如01)。然后它们会随心所欲地扩展。

    使用sort(正确):

    mapfile -td '' myfiles <(printf '%s\0' * | sort -zn)
    
    for file in "${myfiles[@]}"; do
        # what you were going to do
    

    sort -z 用于零/空终止行很常见,但不是 posix。它使包含新行的处理路径/数据变得安全。没有-z

    mapfile -t myfiles <(printf '%s\n' * | sort -n)
    # Rest is the same.
    

    重命名目标文件:

    #!/bin/bash
    
    cd /path/to/the/number/files || exit 1
    
    # Gets length of the highest number. Or you can just hardcode it.
    length=$(printf '%s\n' * | sort -n | tail -n 1)
    length=${#length}
    
    for i in *; do
        mv -n "$i" "$(printf "%.${length}d" "$i")"
    done
    

    使用零填充数字创建新文件的示例:

    touch {000..100} # Or
    for i in {000..100}; do
        > "$i"
    done
    

    如果是您的脚本制作了目标文件,则可以使用$(printf %.Nd [file]) 之类的东西在您写入之前填充名称。但是您需要先知道最大数字的字符长度(N)。

    【讨论】:

      【解决方案2】:

      顺便说一句,如果您可以选择并且提前工作一次比每次都排序更可取,您还可以使用前导零来格式化目录名称。如果可能,这通常是更好的设计。

      我做了一些比较。

      $: echo [0-9][0-9]/ # perfect list based on default string sort
      00/ 01/ 02/ 03/ 04/ 05/ 06/ 07/ 08/ 09/ 10/ 11/ 12/ 13/ 14/ 15/ 16/ 17/ 18/ 19/ 20/
      

      这也会过滤掉任何非数字名称和任何非目录。

      $: for d in [0-9][0-9]/; do echo "${d%/}"; done
      00
      01
      02
      03
      04
      05
      06
      07
      08
      09
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      

      如果我同时显示一位数和两位数的版本(我都制作了)

      $: shopt -s extglob
      $: echo @(?|??)
      0 00 01 02 03 04 05 06 07 08 09 1 10 11 12 13 14 15 16 17 18 19 2 20 3 4 5 6 7 8 9
      

      只有不带前导零的个位数版本会出现故障。

      【讨论】:

        【解决方案3】:

        如果您有 GNU sort,则使用 -V 标志。

        for file in /dir/* ; do echo "$file" ; done | sort -V
        

        或者将数据存储在一个数组中。

        files=(/dir/*); printf '%s\n' "${files[@]}" | sort -V
        

        【讨论】:

          【解决方案4】:

          目录按字母顺序排序。所以“10”在“2”之前。 如果我列出 20 个名称对应于前 20 个整数的文件,我会得到:

          1  10  11  12  13  14  15  16  17  18  19  2  20  3  4  5  6  7  8  9
          

          我可以调用函数'sort -n',所以我会按数字而不是字母顺序对它们进行排序。以下命令:

          for i in $(ls | sort -n) ; do echo $i ; done
          

          产生以下输出:

          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          16
          17
          18
          19
          20
          

          即你的命令:

          for file in /dir/*
          

          应该重写: 对于“dir/”中的文件$(ls /dir/* | sort -n)

          【讨论】:

          • array=($(ls dir/ | sort -h)) for file in "${array[@]}"; 这就是我最终坚持的,尽管它不那么优雅。不管怎样,感谢您的贡献!
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-05-08
          • 2015-09-20
          • 2013-08-31
          • 2018-12-15
          • 1970-01-01
          相关资源
          最近更新 更多