Iterate through list of filenames in order they were created in bash

前端 未结 8 1735
深忆病人
深忆病人 2020-12-05 19:27

Parsing output of ls to iterate through list of files is bad. So how should I go about iterating through list of files in order by which they were first created

相关标签:
8条回答
  • 2020-12-05 19:43

    You can try using use stat command piped with sort:

    stat -c '%Y %n' * | sort -t ' ' -nk1 | cut -d ' ' -f2-
    

    Update: To deal with filename with newlines we can use %N format in stat andInstead of cut we can use awk like this:

    LANG=C stat -c '%Y^A%N' *| sort -t '^A' -nk1| awk -F '^A' '{print substr($2,2,length($2)-2)}'
    
    1. Use of LANG=C is needed to make sure stat uses single quotes only in quoting file names.
    2. ^A is conrtrol-A character typed using ControlVA keys together.
    0 讨论(0)
  • 2020-12-05 19:45
    sorthelper=();
    for file in *; do
        # We need something that can easily be sorted.
        # Here, we use "<date><filename>".
        # Note that this works with any special characters in filenames
    
        sorthelper+=("$(stat -n -f "%Sm%N" -t "%Y%m%d%H%M%S" -- "$file")"); # Mac OS X only
        # or
        sorthelper+=("$(stat --printf "%Y    %n" -- "$file")"); # Linux only
    done;
    
    sorted=();
    while read -d $'\0' elem; do
        # this strips away the first 14 characters (<date>) 
        sorted+=("${elem:14}");
    done < <(printf '%s\0' "${sorthelper[@]}" | sort -z)
    
    for file in "${sorted[@]}"; do
        # do your stuff...
        echo "$file";
    done;
    

    Other than sort and stat, all commands are actual native Bash commands (builtins)*. If you really want, you can implement your own sort using Bash builtins only, but I see no way of getting rid of stat.

    The important parts are read -d $'\0', printf '%s\0' and sort -z. All these commands are used with their null-delimiter options, which means that any filename can be procesed safely. Also, the use of double-quotes in "$file" and "${anarray[*]}" is essential.

    *Many people feel that the GNU tools are somehow part of Bash, but technically they're not. So, stat and sort are just as non-native as perl.

    0 讨论(0)
  • 2020-12-05 19:46

    It may be a little more work to ensure it is installed (it may already be, though), but using zsh instead of bash for this script makes a lot of sense. The filename globbing capabilities are much richer, while still using a sh-like language.

    files=( *(oc) )
    

    will create an array whose entries are all the file names in the current directory, but sorted by change time. (Use a capital O instead to reverse the sort order). This will include directories, but you can limit the match to regular files (similar to the -type f predicate to find):

    files=( *(.oc) )
    

    find is needed far less often in zsh scripts, because most of its uses are covered by the various glob flags and qualifiers available.

    0 讨论(0)
  • 2020-12-05 19:48

    Each file has three timestamps:

    1. Access time: the file was opened and read. Also known as atime.
    2. Modification time: the file was written to. Also known as mtime.
    3. Inode modification time: the file's status was changed, such as the file had a new hard link created, or an existing one removed; or if the file's permissions were chmod-ed, or a few other things. Also known as ctime.

    Neither one represents the time the file was created, that information is not saved anywhere. At file creation time, all three timestamps are initialized, and then each one gets updated appropriately, when the file is read, or written to, or when a file's permissions are chmoded, or a hard link created or destroyed.

    So, you can't really list the files according to their file creation time, because the file creation time isn't saved anywhere. The closest match would be the inode modification time.

    See the descriptions of the -t, -u, -c, and -r options in the ls(1) man page for more information on how to list files in atime, mtime, or ctime order.

    0 讨论(0)
  • 2020-12-05 19:49

    I've just found a way to do it with bash and ls (GNU).
    Suppose you want to iterate through the filenames sorted by modification time (-t):

    while read -r fname; do
        fname=${fname:1:((${#fname}-2))} # remove the leading and trailing "
        fname=${fname//\\\"/\"}          # removed the \ before any embedded "
        fname=$(echo -e "$fname")        # interpret the escaped characters
        file "$fname"                    # replace (YOU) `file` with anything
    done < <(ls -At --quoting-style=c)
    

    Explanation

    Given some filenames with special characters, this is the ls output:

    $ ls -A
     filename with spaces   .hidden_filename  filename?with_a_tab  filename?with_a_newline  filename_"with_double_quotes"
    
    $ ls -At --quoting-style=c
    ".hidden_filename"  " filename with spaces "  "filename_\"with_double_quotes\""  "filename\nwith_a_newline"  "filename\twith_a_tab"
    

    So you have to process a little each filename to get the actual one. Recalling:

    ${fname:1:((${#fname}-2))} # remove the leading and trailing "
    # ".hidden_filename" -> .hidden_filename
    ${fname//\\\"/\"}          # removed the \ before any embedded "
    # filename_\"with_double_quotes\" -> filename_"with_double_quotes"
    $(echo -e "$fname")        # interpret the escaped characters
    # filename\twith_a_tab -> filename     with_a_tab
    

    Example

    $ ./script.sh
    .hidden_filename: empty
     filename with spaces : empty
    filename_"with_double_quotes": empty
    filename
    with_a_newline: empty
    filename    with_a_tab: empty
    

    As seen, file (or the command you want) interprets well each filename.

    0 讨论(0)
  • 2020-12-05 19:51

    How about a solution with GNU find + sed + sort?

    As long as there are no newlines in the file name, this should work:

    find . -type f -printf '%T@ %p\n' | sort -k 1nr | sed 's/^[^ ]* //'
    
    0 讨论(0)
提交回复
热议问题