I have a directory with filenames of the format $date.txt. I would like to cat 5 latest files from it. Is there a more elegant solution to that, than
for f in 2*.txt; do echo $f; done | tail -5 | while read f; do cat $f; done
In ksh93
, bash
or zsh
:
files=( 2*.txt )
cat "${files[@]: -5}"
This would create an array of the filenames matching the pattern 2*.txt
. It would then output the contents of the last five of these.
In zsh
, you can also specify a range of files as part of its glob qualifiers:
cat 2*.txt([-5,-1])
In any POSIX shell, this may also be done through
set -- 2*.txt
while [ "$#" -gt 5 ]; do shift; done # or: [ "$#" -gt 5 ] && shift "$(( $# - 5 ))"
cat "$@"
This sets the positional parameters to the filenames matching the pattern. It then shifts off the names from the beginning of the list until the list only has five elements in it. cat
is then invoked on the remaining filenames.
In all of these solutions, the files would be sorted lexicographically. Filenames with spaces or newlines are handled correctly.
shift $(($# - 5))
might also work to shift to the last 5 - it may complain if fewer than 5 files were found.
[ "$#" -gt 5 ] && shift "$(( "$#" - 5 ))"
maybe.
I would use a while read loop, this would handle files with spaces.
ls 2*.txt | tail -5 | while read loop
do
cat "$loop"
done
If you always want the 5 most recent you could change it to ls -tr | tail -5
Based on the question, with no prospect of spaces and 2*.txt happily returning correct ordering, how about
cat $(ls 2*.txt|tail -5)
YYYY MM DD
, with spaces.
ls
doesn't really do anything here, I'd usually recommend something else in it's place. But printf "%s\n" 2*.txt
is a bit hairy to write. Just make sure you don't have ls
aliased to anything funny if you do this in an interactive shell.
As mentioned latest
in question, we can check with the modification time of files in the directory.
cat $(ls -ltr *.txt | tail -n5 | awk '{print $NF}' | tr '\n' ' ')
ls -ltr
- will list files with respect to modification timestamp (reverse)
tail -n5
- will take last 5 files
$NF
- last field
tr
- transpose column to row
-l
from ls
, the awk
along with it, and the tr
is unnecessary too. So cat $(ls -tr *.txt | tail -n5)
should do, with all the usual problems with whitespace and glob characters in file names.
$date
? Is it something likeWed May 30 23:28:06 BST 2018
? Or2018-05-30
? Something else? Does it have spaces? How can$date.txt
match more than one file?