Search code examples
bashsortingfilenamesls

Bash: list files ordered by the number they contain


Goal

I have a series of files that have various names. Each file's name contain a unique integer number made of between 1 and 4 digits. I would like to order the files (by displaying their full name) but ordered by the number they contain.

Example

The files ..

Hello54.txt
piou2piou.txt
hap_1002_py.txt
JustAFile_78.txt
darwin2012.txt

.. should be listed as

piou2piou.txt
Hello54.txt
JustAFile_78.txt
hap_1002_py.txt
darwin2012.txt

Solution

  • You just need to sort by the numeric part. One approach might be to eliminate the non-numeric part and build an array. On the upside, bash has sparse arrays, so whatever order you add the members in, they will come out in the correct order. Pseudo code:

    array[name_with_non_numerics_stripped]=name
    print array
    

    My quick and not careful implementation:

    sortnum() {
        # Store the output in a sparse array to get implicit sorting
        local -a map
        local key
        # (REPLY is the default name `read` reads into.)
        local REPLY 
        while read -r; do
            # Substitution parameter expansion to nuke non-numeric chars
            key="${REPLY//[^0-9]}"
            # If key occurs more than once, you will lose a member.
            map[key]="$REPLY"
        done
        # Split output by newlines.
        local IFS=$'\n'
        echo "${map[*]}"
    }
    

    If you have two members with the same "numeric" value, only the last one will be printed. @BenjaminW suggested appending such entries together with a newline. Something like…

    [[ ${map[key]} ]] && map[key]+=$'\n'
    map[key]+="$REPLY"
    

    …in place of the map[key]="$REPLY" above.