6

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
3
  • 4
    What is $date? Is it something like Wed May 30 23:28:06 BST 2018? Or 2018-05-30? Something else? Does it have spaces? How can $date.txt match more than one file?
    – terdon
    Commented May 30, 2018 at 22:28
  • 6
    Thanks for the edit, but the specific format of the date is also relevant. Especially if it can have spaces. Please give us a few examples. Numerical sorting won't always be the same as sorting by date, depending on the format used.
    – terdon
    Commented May 30, 2018 at 22:43
  • I also don't understand why it's put on hold as unclear. I've specified the behavior, and got several good solutions, so apparently people understand the question.
    – LazyCat
    Commented Jun 1, 2018 at 12:48

4 Answers 4

7

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.

3
  • shift $(($# - 5)) might also work to shift to the last 5 - it may complain if fewer than 5 files were found.
    – muru
    Commented May 31, 2018 at 6:09
  • @muru Yes, something like [ "$#" -gt 5 ] && shift "$(( "$#" - 5 ))" maybe.
    – Kusalananda
    Commented May 31, 2018 at 8:42
  • Thanks, it works fine, and I also like the expressive syntax.
    – LazyCat
    Commented May 31, 2018 at 13:43
2

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

1

Based on the question, with no prospect of spaces and 2*.txt happily returning correct ordering, how about

cat $(ls 2*.txt|tail -5)
3
  • Unless the filenames are MMDDYYYY, or worse, YYYY MM DD, with spaces.
    – Jeff Schaller
    Commented May 30, 2018 at 23:21
  • @Jeff, sure, now qualified
    – steve
    Commented May 31, 2018 at 5:25
  • Given that 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.
    – ilkkachu
    Commented Jun 1, 2018 at 9:01
0

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

3
  • 2
    Why the -l, seems unnecessary
    – steve
    Commented May 31, 2018 at 5:22
  • You can lose the -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.
    – ilkkachu
    Commented Jun 1, 2018 at 9:03
  • @illkkachu You are right, but that will list files in ascending order rather than lastest.
    – Siva
    Commented Jun 1, 2018 at 9:06

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .