zsh – How to Sort File Names in Numerical and Modified Time Order

filespdfsortzsh

I want to join pdf files by pdfjoin/pdfunite/… in the numerical order discussed well in the thread answer linux command merge pdf files with numerical sort and Modified time order.
If you use the solution in the thread, it puts the order in the numerical order and alphabetical order.
This is problematic with the filenames such as where you see both have the same Modified time by minute accuracy but Visceral is earlier by second accuracy (File browser notes it and puts Visceral first in the Modified order.

Filename               Modified
-----                  ---
3.THE ABC.pdf          10:39 
3.Visceral abc..pdf    10:39

Complete filenames

1.Description abc.pdf
2.Gabcd.pdf
3.THE ABC.pdf
3.Visceral abc..pdf
4.description of abc.pdf
5.Chraa..pdf

Proposal #1 works in the numerical and alphabetical order but not in the numerical and modified order

# https://stackoverflow.com/a/23643544/54964
ls -v *.pdf | ...
    bash -c 'IFS=$'"'"'\n'"'"' read -d "" -ra x;pdfunite "${x[@]}" output.pdf'

Proposal #2 simplified case but does not deal whitespaces and other special characters in filenames

# https://stackoverflow.com/a/23643544/54964
pdfunite $(ls *.pdf | sort -n) output.pdf

There is nothing in the pdfunite --help about the ordering so I think it should be done by ls/sort/…
The command sort does not have anything about modified in its man page.

Testing xhienne's answer

The order is not correct in the output where you see 2.jpg and 4.jpg are at the wrong order for some reason

masi@masi:~/Documents$ ls -tr /home/masi/Documents/[0-9]* | sort -t. -k1,1n -s
/home/masi/Documents/1.jpg
/home/masi/Documents/3.jpg
/home/masi/Documents/5.jpg
/home/masi/Documents/6.jpg
/home/masi/Documents/7.jpg
/home/masi/Documents/8.jpg
/home/masi/Documents/9.jpg
/home/masi/Documents/10.jpg
/home/masi/Documents/2.jpg
/home/masi/Documents/4.jpg

2nd iteration

export LC_ALL=C; ls -tr /home/masi/Documents/[0-9]* | sort -t. -k1,1n -s

Output

/home/masi/Documents/1.jpg
/home/masi/Documents/3.jpg
/home/masi/Documents/5.jpg
/home/masi/Documents/6.jpg
/home/masi/Documents/7.jpg
/home/masi/Documents/8.jpg
/home/masi/Documents/9.jpg
/home/masi/Documents/10.jpg
/home/masi/Documents/2.jpg
/home/masi/Documents/4.jpg

OS: Debian 8.5

Best Answer

You could do that with zsh:

zmodload zsh/stat

prefixmtime () {
sortstring=${(l:6::0:)${REPLY%%.*}}$(zstat -F '%s' +mtime -- $REPLY)
REPLY=${sortstring}
}

print -rl -- *(o+prefixmtime)

Replace print -rl with your command if you're happy with the result


How it works:
The globs will sort here (via o+function) based on what the function prefixmtime returns, that is sortstring which is a string obtained by concatenating the numerical prefix of each file name ${REPLY%%.*} left-padded with zeros (l:6::0:) (assuming prefixes are up to 6-chars long) and the mtime in seconds (obtained via zstat module). It may be easier to understand how it works if you run:

{ for f (*)
printf '%s %s\n' ${(l:6::0:)${f%%.*}}$(zstat -F '%s' +mtime -- $f) $f
} | sort -k1,1n

Note that the above assumes you're in the same directory with your files, otherwise you'll have to define the sort string in that function as

sortstring=${(l:6::0:)${${REPLY##*/}%%.*}}$(zstat -F '%s' +mtime -- $REPLY)

and then you can use directory paths e.g.

print -rl some/place/else/*(o+prefixmtime)
Related Question