Find and display most recent files using find and perl

find $HOME -type f -print0 | perl -0 -wn -e '@f=<>; foreach $file (@f){ (@el)=(stat($file)); push @el, $file; push @files,[ @el ];} @o=sort{$a->[9]<=>$b->[9]} @files; for $i (0..$#o){print scalar localtime($o[$i][9]), "\t$o[$i][-1]\n";}'|tail
This pipeline will find, sort and display all files based on mtime. This could be done with find | xargs, but the find | xargs pipeline will not produce correct results if the results of find are greater than xargs command line buffer. If the xargs buffer fills, xargs processes the find results in more than one batch which is not compatible with sorting. Note the "-print0" on find and "-0" switch for perl. This is the equivalent of using xargs. Don't you love perl? Note that this pipeline can be easily modified to any data produced by perl's stat operator. eg, you could sort on size, hard links, creation time, etc. Look at stat and just change the '9' to what you want. Changing the '9' to a '7' for example will sort by file size. A '3' sorts by number of links.... Use head and tail at the end of the pipeline to get oldest files or most recent. Use awk or perl -wnla for further processing. Since there is a tab between the two fields, it is very easy to process.
Sample Output
find $HOME -type f -print0 | perl -0 -wn -e '@f=<>; foreach $file (@f){ (@el)=(stat($file)); push @el, $file; push @files,[ @el ];} @o=sort{$a->[9]<=>$b->[9]} @files; for $i (0..$#o){print scalar localtime($o[$i][9]), "\t$o[$i][-1]\n";}'|tail
Mon Sep 21 19:24:47 2009	/Users/andrew/Library/Logs/Sync/mobilemesyncstats.plist
Mon Sep 21 19:24:47 2009	/Users/andrew/Library/Preferences/ByHost/com.apple.DotMacSync.0019e366823e.plist
Mon Sep 21 19:24:48 2009	/Users/andrew/Library/Preferences/ByHost/com.apple.AOSNotification.0019e366823e.plist
Mon Sep 21 19:24:54 2009	/Users/andrew/Library/Cookies/Cookies.plist
Mon Sep 21 19:25:03 2009	/Users/andrew/Library/Preferences/com.apple.Terminal.plist
Mon Sep 21 19:26:44 2009	/Users/andrew/Library/Mail/IMAP-andrew@x-fifteen.com@mail.x-fifteen.com/Sent Messages.imapmbox/Messages/311191.emlx
Mon Sep 21 19:26:47 2009	/Users/andrew/Library/Logs/Sync/syncservices.log
Mon Sep 21 19:26:55 2009	/Users/andrew/Library/Preferences/com.apple.mail.plist
Mon Sep 21 19:27:48 2009	/Users/andrew/Library/Mail/IMAP-andrew@x-fifteen.com@mail.x-fifteen.com/INBOX.imapmbox/Messages/311201.emlx
Mon Sep 21 19:27:49 2009	/Users/andrew/Library/Mail/Envelope Index

3
By: drewk
2009-09-21 22:11:16

These Might Interest You

  • I find it very handy to be able to quickly see the most recently modified/created files in a directory. Note that the "q" option will reveal any files with non-printable characters in their filename. Show Sample Output


    7
    ls -qaltr # list directory in chronological order, most recent files at end of list
    mpb · 2013-02-25 14:14:44 1
  • syntax follows regular command line expression. example: let's say you have a directory (with subdirs) that has say 4000 .php files. All of these files were made via script, but uh-oh, there was a typo! if the typo is "let's go jome!" but you meant it to say "let's go home!" find . -name "*.php" | xargs perl -pi -e "s/let\'s\ go\ jome\!/let\'s\ go\ home\!/g" all better :) multiline: find . -name "*.php" | xargs perl -p0777i -e 's/knownline1\nknownline2/replaced/m' indescriminate line replace: find ./ -name '*.php' | xargs perl -pi -e 's/\".*$\"/\new\ line\ content/g' Show Sample Output


    6
    find . -name "*.txt" | xargs perl -pi -e 's/old/new/g'
    neztach · 2009-02-06 00:28:03 3
  • Finds all *.p[ml]-files and runs a perl -c on them, checking whether Perl thinks they are syntactically correct Show Sample Output


    0
    for code in $(find . -type f -name '*.p[ml]'); do perl -c "$code"; done
    udog · 2010-05-29 23:26:40 1
  • Provides a much cleaner, easier to read output than the closest alternative, ls -1R. This alternative makes it easier to differentiate directories from files: find . -printf '%y %p\n' | perl -ne 'if( m/(\w) (.*)\/(.*)/ ) { $t = $1; $p = $2; $f = $3; $t =~ s/[^d]/ /; $p =~ s/[^\/]/ /g; $p =~ s/\//|/g; print "$t $p/$f\n"; } elsif( m/(\w) (.*)/ ) { print "$1 $2\n"; } else { print "error interpreting: \"$_\"\n"; }' Show Sample Output


    0
    find . -printf '%p\n' | perl -ne 'if( m/(.*)\/(.*)/ ) { $p = $1; $f = $2; $p =~ s/[^\/]/ /g; $p =~ s/\//|/g; print "$p/$f\n"; } elsif( m/(.*)/ ) { print "$1\n"; } else { print "error interpreting: \"$_\"\n"; }'
    cbetti · 2012-04-24 19:51:00 0

What Others Think

A quick google reveals that "9" means "access time" http://perldoc.perl.org/functions/stat.html . To avoid the multiple batches problem in the shell, you could use find to do the stat()ing for you via -printf, and then simply pipe it into a sort (don't you love shell?): find $HOME -type f -print "%+\t%f\n" | sort -n this is a good approximation, although there's something different between find's notion of atime and perl's...I'm probably missing something...
bwoodacre · 456 weeks and 5 days ago
I don't follow in this case.
drewk · 456 weeks and 5 days ago
Sorry on previous comment. I was looking at the wrong comment... Perl arrays are zero based, so the 10th element of the stat return is array index 9... The command you gave find $HOME -type f -print "%+\t%f\n" | sort -n does not work on OS X. :-( When I get time, I will debug...
drewk · 456 weeks and 5 days ago
Sometimes you Linux guys have all the fun. You have: find $HOME -type f -print "%+\t%f\n" | sort -n If think you meant: find $HOME -type f -printf "%+\t%f\n" | sort -n The printf option is not available on OS X (and I don't think on BSD). One could use: find $HOME -type f -exec printf "format" {} \; | sort -n but I have 350,000 files in $HOME, and therefor 350,000 subshells. Slow. On Linux with -printf available as an option for find, your solution works well -- I think. I do not have Linux to test.
drewk · 456 weeks and 5 days ago

What do you think?

Any thoughts on this command? Does it work on your machine? Can you do the same thing with only 14 characters?

You must be signed in to comment.

What's this?

commandlinefu.com is the place to record those command-line gems that you return to again and again. That way others can gain from your CLI wisdom and you from theirs too. All commands can be commented on, discussed and voted up or down.

Share Your Commands



Stay in the loop…

Follow the Tweets.

Every new command is wrapped in a tweet and posted to Twitter. Following the stream is a great way of staying abreast of the latest commands. For the more discerning, there are Twitter accounts for commands that get a minimum of 3 and 10 votes - that way only the great commands get tweeted.

» http://twitter.com/commandlinefu
» http://twitter.com/commandlinefu3
» http://twitter.com/commandlinefu10

Subscribe to the feeds.

Use your favourite RSS aggregator to stay in touch with the latest commands. There are feeds mirroring the 3 Twitter streams as well as for virtually every other subset (users, tags, functions,…):

Subscribe to the feed for: