Search code examples
linuxbashunix-timestampsambaext4

How can I sort a directory by creation date in BASH?


I'm trying to create a simple script to list the 16 most recent folders created in a directory on my nas machine as a way to display the most recent movies added to my collection.

the script i am using at the moment is:

#!/bin/bash
rm -f /volume1/new-movies/*
IFS=$'\x0A'
fresh=$(ls -1ct /volume1/movies | head -16)
for folder in $fresh
do
    file=$(find "/volume1/movies/$folder" -maxdepth 1 -type f)
    movie=$(basename "$file")
    ln -s "$file" "/volume1/new-movies/$movie"
done
ls -1 /volume1/new-movies

which is OK (the movies folder will only ever contain folders). My problem is this is sorted by the file/folders modification time rather than the creation time.

the filesystem is ext4 and should support a birth time but i have had no luck accessing it.

scott@pandora scripts $ stat  /volume1/movies/example/
  File: '/volume1/movies/example/'
  Size: 4096            Blocks: 8          IO Block: 4096   directory
Device: 902h/2306d      Inode: 373800961   Links: 2
Access: (0755/drwxr-xr-x)  Uid: ( 1028/   scott)   Gid: (  100/   users)
Access: 2013-04-09 13:39:53.243991684 +1000
Modify: 2013-04-06 13:26:00.965998952 +1100
Change: 2013-04-09 11:46:23.280991727 +1000
 Birth: -

however, samba seems to have no issue displaying the correct creation date/time. is there a way to access the same information from bash? or am i going to have to program something in python/other to do what i need by accessing smb directly and listing each folder with the creation date?

scott@pandora scripts $ smbclient \\\\localhost\\movies\\
Enter scott's password:
Domain=[EXAMPLENET] OS=[Unix] Server=[Samba 3.6.9]
smb: \> allinfo "example"
altname: E06KNE~A
create_time:    Fri Jun 18 17:23:49 2010 EST
access_time:    Tue Apr  9 13:39:53 2013 EST
write_time:     Sat Apr  6 13:26:01 2013 EST
change_time:    Sat Apr  6 13:26:01 2013 EST
attributes: DA (30)
smb: \> quit

edit: see my below answer for my final solution to this issue.


Solution

  • After some fiddling I have come up with a solution which appears to work a little better than trying to rely on ls -lc. here's the full script i am now using.

    #!/usr/bin/env sh
    
    # remove the existing symbolic links-
    rm -f /volume1/new-movies/*
    
    # retrieve the folder listing from the smbclient (the date used is 'modified'
    # which appears closer to 'creation' than anything else)
    folders=$(smbclient \\\\localhost\\movies -N -c ls 2>/dev/null)
    
    # format the output for better sorting. eg
    # folder DA 0 Mon  2 Oct 12:23:00 2012
    # 2012-Oct-2 12:23:00 "folder"
    fmt1=$(echo "$folders" | sed -E 's/^  (.*\))\s+DA.* (\w+)\s+([0-9]{1,2})\s+([0-9:]{8})\s+([0-9]{4})$/\5-\2-\3 \4 "\1"/')
    
    # change the month names to numeric representations and pad single digit
    # dates to 2 characters. eg.
    # 2012-Oct-2 12:23:00 "folder"
    # 2012-10-02 12:23:00 "folder"
    fmt2=$(echo -e "$fmt1" |sed 's/-Jan-/-01-/;s/-Feb-/-02-/;s/-Mar-/-03-/;s/-Apr-/-04-/;s/-May-/-05-/;s/-Jun-/-06-/;s/-Jul-/-07-/;s/-Aug-/-08-/;s/-Sep-/-09-/;s/-Oct-/-10-/;s/-Nov-/-11-/;s/-Dec-/-12-/;s/-\([0-9]\) /-0\1 /')
    
    # sort the folders in reverse order
    sortd=$(echo -e "$fmt2" | sort -r)
    
    # grab the last 16. (16 items per page are displayed on the wd tv streaming)
    latest=$(echo -e "$sortd" | head -16 | cut -d\" -f2)
    
    # loop through each folder using new line rather than dft. space
    IFS=$'\x0A'
    for l in $latest
    do
       # find the movie file in the directory, dont look in subdirectories and
       # only match movie files. skips subtitles, bonus features, etc.
       f=$(find "/volume1/movies/$l" -maxdepth 1 -type f \
       \( -iname \*.avi -o \
          -iname \*.mp4 -o \
          -iname \*.mkv -o \
          -iname \*.mpg -o \
          -iname \*.ts -o \
          -iname \*.m4v \
       \) -exec echo "{}" \;)
    
       # grab just the filename
       b=$(basename "$f")
    
       # link the file $f to the new movie folder with just the base name.
       ln -s "$f" "/volume1/new-movies/$b"
    
    done
    IFS=
    ls -1 /volume1/new-movies