【问题标题】:How can I sort filenames within multiple directories into one sequential and numerically ascending array/list?如何将多个目录中的文件名排序为一个顺序且数字升序的数组/列表?
【发布时间】:2018-01-15 06:31:16
【问题描述】:

假设我有三个目录,每个目录中都有不同数量的文件(尽管在这个简化的例子中,它是四个):

BA-2016-05:

AG-1829A.jpg
AG-1829B.jpg
AG-1829C.jpg
AG-1830A.jpg

BA-2016-V01:

AG-1712A.jpg
AG-1712B.jpg
AG-1922A.jpg
AG-1922B.jpg

BA-2017-PD02:

AG-1100A.jpg
AG-1100B.jpg
AG-1100C.jpg
AG-1100D.jpg

我希望结果数组看起来像这样:

AG-1100A.jpg AG-1100B.jpg AG-1100C.jpg AG-1100D.jpg
AG-1712A.jpg AG-1712B.jpg
AG-1829A.jpg AG-1829B.jpg AG-1829C.jpg
AG-1830A.jpg
AG-1922A.jpg AG-1922B.jpg

数组将被保存到.txt 文档中,可以用空格或制表符分隔。

到目前为止,我已经稍微调整了 a response from elsewhere online 以按文件名升序列出所有排序的文件:

find ~/BA* -iname "*.jpg" |\
awk -vFS=/ -vOFS=/ '{ print $NF,$0 }' |\
sort -n -t / |\
cut -f2- -d/

使用filename="${fullpath##*/}" 切断路径的开头应该很容易,但在那之后我就卡住了。如何将此列表转换为上述格式的数组?

几点说明:

  1. 文件名的格式将始终为AG-[numbers][A-D],或者为了更通用,为[letters][hyphen][numbers][A-D]。
  2. 扩展名将始终为 .jpg 或 .JPG,但适用于所有扩展名并将其保留在输出数组中的扩展名会加分。

编辑:我在下面包含了我正在使用的最终解决方案。它包括我得到的两个答案的混合内容,以及一些花哨的 awk 东西,然后输出用于更改制表符的空格。奇迹般有效。我还意识到我实际上需要包含一个 URL,该 URL 将通过将文件名/路径合并到其中来完成。但我很快就弄清楚了。无论如何,感谢大家的帮助,这是最终代码:

#!/bin/bash

# The number of the current line
current_nb=;

# Variable to store the current line before writing it
line=;

# Loop through all regular files of the directories and subdirectories specified
# Sort all file paths in ascending order (irrespective of the directory name)
for file in $(find ./BA* -iname "*.jpg" -printf '%f/%p\n' | sort -n -t / | cut -f2- -d/); 
do 

    # Append image URL to each file in the loop
    file_url=`sed 's/^.*\/\(.*\/.*\)/[INSERT URL HERE]/\1/' <<< "$file"`;

    # Extract the number from the current file in the loop
    nb=`sed 's/.*-\([0-9]\+\)[[:alpha:]].*/\1/' <<< "$file"`; 

    # For the first loop, when $current_nb is not initialized, we set $nb as the default value
    current_nb=${current_nb:-$nb}; 

    # If we stay on the same line...
    if [ "$nb" -eq "$current_nb" ]; 
        then 
        # ...then concatenate the new filename with the line currently being created
        line="$line $file_url"; 

        else 
        # Otherwise, append the line at the end of the output file (changing spaces to tabs)...
        echo $line | awk -v OFS="\t" '$1=$1' >> url_list.txt; 

        # ...and prepare a new line
        line="$file_url ";
        current_nb=$nb; 
    fi; 

done;

【问题讨论】:

    标签: arrays bash sorting unix


    【解决方案1】:

    排序列表

    生成您想要的列表:

    $ find ./BA* -iname "*.jpg" -printf '%f\n' | sort -n
    AG-1100A.jpg
    AG-1100B.jpg
    AG-1100C.jpg
    AG-1100D.jpg
    AG-1712A.jpg
    AG-1712B.jpg
    AG-1829A.jpg
    AG-1829B.jpg
    AG-1829C.jpg
    AG-1830A.jpg
    AG-1922A.jpg
    AG-1922B.jpg
    

    Find 的printf 功能允许自定义输出。由于您只需要带有目录的文件名,因此我们使用%f 格式选项来代替-printf。

    创建数组(naive 版本)

    如果保证文件名不包含空格或任何其他 shell 活动字符,则以下工作:

    arr=($(find ./BA* -iname "*.jpg" -printf '%f\n' | sort -n))
    

    我们可以通过以下方式验证数组 arr 是否包含您想要的内容:

    $ declare -p arr
    declare -a arr=([0]="AG-1100A.jpg" [1]="AG-1100B.jpg" [2]="AG-1100C.jpg" [3]="AG-1100D.jpg" [4]="AG-1712A.jpg" [5]="AG-1712B.jpg" [6]="AG-1829A.jpg" [7]="AG-1829B.jpg" [8]="AG-1829C.jpg" [9]="AG-1830A.jpg" [10]="AG-1922A.jpg" [11]="AG-1922B.jpg")
    

    创建数组(健壮版)

    处理最通用的文件名:

    array=()                                                                            
    while IFS= read -r -d $'\0'; do                                                     
       array+=("$REPLY")                                                               
    done < <(find ./BA* -iname "*.jpg" -printf '%f\0' | sort -zn)  
    

    验证结果:

    $ declare -p array
    declare -a array=([0]="AG-1100A.jpg" [1]="AG-1100B.jpg" [2]="AG-1100C.jpg" [3]="AG-1100D.jpg" [4]="AG-1712A.jpg" [5]="AG-1712B.jpg" [6]="AG-1829A.jpg" [7]="AG-1829B.jpg" [8]="AG-1829C.jpg" [9]="AG-1830A.jpg" [10]="AG-1922A.jpg" [11]="AG-1922B.jpg")
    

    健壮的版本用 NUL 字符分隔文件名。可以在here 找到有关其工作原理的完整说明。

    【讨论】:

    • 我真的很喜欢数组创建的简单性以及-printf 的功能(我对此很陌生)!我在最终代码的管道中包含了该排序样式,因为我最终需要通过文件名对其进行排序,同时仍然包含目录。谢谢您的帮助!如果您想查看或建议更正/更改,我已在原始帖子的正文中发布了我的脚本的最终版本。
    【解决方案2】:

    这更通用,适用于所有扩展。 另外,我没有创建任何数组,而是将结果直接写入输出文件。

    #!/bin/bash
    # The number of the current line
    current_nb=;
    # Variable to store the current line before writing it
    line=;
    # Loop through all regular files of this directory and its subdirs sorted
    # We extract the basename (e.g. AG-1829A.jpg )
    for file in $(find . -type f -exec basename {} \; | sort -n); do 
        # Extract its number
        nb=`sed 's/.*-\([0-9]\+\)[[:alpha:]].*/\1/' <<<"$file"`; 
        # For the first loop, when current_nb is not initialized, we set $nb as default value
        current_nb=${current_nb:-$nb}; 
        # If we stay on the same line
        if [ "$nb" -eq "$current_nb" ]; then 
            # Concatenate the new filename
            line="$line $file"; 
        else 
            # Else append the line at the end of file
            echo $line >> out.txt; 
            # And prepare the new one
            line="$file ";
            current_nb=$nb; 
        fi; 
    done;
    

    【讨论】:

    • 这很好用!它准确地产生了我需要的文件结构。我对其进行了一些调整(更改了find 部分,添加了一些管道等)并在我的原始帖子中发布了最终代码。很好的解决方案!谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-08
    • 1970-01-01
    • 1970-01-01
    • 2013-11-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多