Linux find sort by date

Unix/Linux find and sort by date modified

How can I do a simple find which would order the results by most recently modified? Here is the current find I am using (I am doing a shell escape in PHP, so that is the reasoning for the variables):

find '$dir' -name '$str'\* -print | head -10 

How could I have this order the search by most recently modified? (Note I do not want it to sort ‘after’ the search, but rather find the results based on what was most recently modified.)

18 Answers 18

find . -printf "%T@ %Tc %p\n" | sort -n 
  • %Tk : File’s last modification time in the format specified by k .
  • @ : seconds since Jan. 1, 1970, 00:00 GMT, with fractional part.
  • c : locale’s date and time (Sat Nov 04 12:02:33 EST 1989).
  • %p : File’s name.

I have this alias for finding recent files in my ~/.zshrc : fr () < find ./ -iname "*"$@"*" -printf "%T@ %Td-%Tb-%TY %Tk:%TM %p\n" | sort -n | cut -d " " -f 2- | grep -i "$@" ; >It recursively finds all files containing the pattern of the first argument passed to the command ( fr ) and sorts them with the most recent one last.

You may want to use ssed to get rid of the seconds fractional part and stil use ISO8601 as @PeterMortensen showed : find . -type f -printf «%TY-%Tm-%TdT%TT %p\n» | sort -r | ssed -R ‘s/^([^.]+)\.\d+ (.*)$/\1 \2/’

@joelostblom JFYI, instead of cut -d » » -f 2- you may want to use cut -d » » -f 1 —complement but it is longer.

Note that for OSX one has to use the GNU version gfind instead of find , since printf is not part of POSIX find . It can be installed with brew install findutils .

The easiest method is to use zsh, thanks to its glob qualifiers.

If you have GNU find, make it print the file modification times and sort by that.

find -type f -printf '%T@ %p\0' | sort -zk 1nr | sed -z 's/^[^ ]* //' | tr '\0' '\n' | head -n 10 

If you have GNU find but not other GNU utilities, use newlines as separators instead of nulls; you’ll lose support for filenames containing newlines.

find -type f -printf '%T@ %p\n' | sort -k 1nr | sed 's/^[^ ]* //' | head -n 10 

If you have Perl (here I’ll assume there are no newlines in file names):

find . -type f -print | perl -l -ne ' $_ = -M; # store file age (mtime - now) END < $,="\n"; @sorted = sort  $_> keys %_; # sort by increasing age print @sorted[0..9]; >' 

If you have Python (also assuming no newlines in file names):

find . -type f -print | python -c 'import os, sys; times = <> for f in sys.stdin.readlines(): f = f[0:-1]; times[f] = os.stat(f).st_mtime for f in (sorted(times.iterkeys(), key=lambda f:times[f], reverse=True))[:10]: print(f)' 

There’s probably a way to do the same in PHP, but I don’t know it.

Читайте также:  Фундаментальные основы linux панин pdf

If you want to work with only POSIX tools, it’s rather more complicated; see How to list files sorted by modification date recursively (no stat command available!) (retatining the first 10 is the easy part).

The above is for python2. If you only have python3, some small changes: python3 -c ‘import os, sys; times = <> for f in sys.stdin.readlines(): f = f[0:-1]; times[f] = os.stat(f).st_mtime for f in (sorted(times.keys(), key=lambda f:times[f], reverse=True))[:10]: print(f);’

Your #2 solution works (it’s the only one I’ve tried). The answer checked as «valid» doen’t work properly (not the last touched files).

You don’t need to PHP or Python, just ls:

man ls: -t sort by modification time -r, reverse order while sorting (--reverse ) -1 list one file per line find /wherever/your/files/hide -type f -exec ls -1rt "<>" +; 

If command * exits with a failure status (ie Argument list too long), then you can iterate with find. Paraphrased from: The maximum length of arguments for a new process

  • find . -print0|xargs -0 command (optimizes speed, if find doesn’t implement «-exec +» but knows «-print0»)
  • find . -print|xargs command (if there’s no white space in the arguments)

If the major part of the arguments consists of long, absolute or relative paths, then try to move your actions into the directory: cd /directory/with/long/path; command * And another quick fix may be to match fewer arguments: command [a-e]*; command [f-m]*; .

The xargs solution does not really work, because it will output in several batches, so you would find the latest file in each batch, as opposed to the latest file of all.

A practical solution with gnu-find can be found in my local-bin repo:

#!/bin/bash function strip_1() < awk ' BEGIN< RS="\0">< $1="" ; gsub(/^\s/,"", $0); print >'; > function strip_1n() < awk '< $1="" ; gsub(/^\s/,"", $0); print >'; > function n2z() < tr \\n \\0; >find=/usr/bin/find if [[ $0 == *latest0 ]]; then $find $ -not -path '*/.git/*' -type f -printf "%T@ %p\0" | sort -nz | n2z | strip_1 | n2z else $find $ -not -path '*/.git/*' -type f -printf "%T@ %p\n" | sort -n | strip_1n fi 

ls doesn’t quote file names in a way xargs can understand (no -0 option, and the various quote styles are inadequate)

find . -type f -printf "%T@\t%Tc %6k KiB %p\n" | sort -n | cut -f 2- 

For each file, this first outputs the numeric timestamp (for sorting by, followed by tabulation \t ), then a human-readable timestamp, then the filesize (unfortunately find ‘s -printf can’t do in mebibytes, only kibibytes), then the filename with relative path.

Then sort -n sorts it by the first numeric field.

Then cut gets rid of that first numeric field which is of no interest to the user. (Prints second field onward.) The default field separator is \t or tabulation.

Thu 06 Feb 2014 04:49:14 PM EST 64 KiB ./057_h2_f7_10/h2_f7_10.class Fri 07 Feb 2014 02:08:30 AM EST 7962976 KiB ./056_h2_f7_400/h2__rh_4e-4.mph Fri 07 Feb 2014 02:23:24 AM EST 7962976 KiB ./056_h2_f7_400/h2_f7_400_out_Model.mph Fri 07 Feb 2014 02:23:24 AM EST 0 KiB ./056_h2_f7_400/h2_f7_400_out.mph.status Fri 07 Feb 2014 02:23:24 AM EST 64 KiB ./056_h2_f7_400/1579678.out Fri 07 Feb 2014 03:47:31 AM EST 8132224 KiB ./057_h2_f7_10/h2__rh_1e-5.mph Fri 07 Feb 2014 04:00:49 AM EST 8132224 KiB ./057_h2_f7_10/h2_f7_10_out_Model.mph Fri 07 Feb 2014 04:00:49 AM EST 0 KiB ./057_h2_f7_10/h2_f7_10_out.mph.status Fri 07 Feb 2014 04:00:49 AM EST 64 KiB ./057_h2_f7_10/1579679.out Fri 07 Feb 2014 09:47:18 AM EST 9280 KiB ./056_h2_f7_400/h2__rh_4e-4.mat Fri 07 Feb 2014 10:51:23 AM EST 9728 KiB ./018_bidomain/h2_plain__rh_1e-5.mat Fri 07 Feb 2014 10:58:33 AM EST 9568 KiB ./057_h2_f7_10/h2__rh_1e-5.mat Fri 07 Feb 2014 05:05:38 PM EST 64 KiB ./058_h2_f7_stationary/h2_f7_stationary.java Fri 07 Feb 2014 06:06:29 PM EST 32 KiB ./058_h2_f7_stationary/slurm.slurm Sat 08 Feb 2014 03:42:07 AM EST 0 KiB ./058_h2_f7_stationary/1581061.err Sat 08 Feb 2014 03:42:14 AM EST 64 KiB ./058_h2_f7_stationary/h2_f7_stationary.class Sat 08 Feb 2014 03:58:28 AM EST 70016 KiB ./058_h2_f7_stationary/h2s__rh_1e-5.mph Sat 08 Feb 2014 04:12:40 AM EST 70304 KiB ./058_h2_f7_stationary/h2s__rh_4e-4.mph Sat 08 Feb 2014 04:12:53 AM EST 70304 KiB ./058_h2_f7_stationary/h2_f7_stationary_out_Model.mph Sat 08 Feb 2014 04:12:53 AM EST 0 KiB ./058_h2_f7_stationary/h2_f7_stationary_out.mph.status Sat 08 Feb 2014 04:12:53 AM EST 32 KiB ./058_h2_f7_stationary/1581061.out Mon 10 Feb 2014 11:40:54 AM EST 224 KiB ./058_h2_f7_stationary/h2s__rh_4e-4.mat Mon 10 Feb 2014 11:42:32 AM EST 224 KiB ./058_h2_f7_stationary/h2s__rh_1e-5.mat Mon 10 Feb 2014 11:50:08 AM EST 32 KiB ./plot_grid.m 

I deliberately made the filesize field 6 characters, because if making it longer, it becomes hard to visually distinguish how large the files are. This way, files larger than 1e6 KiB jut out: by 1 char means 1-9 GB, by 2 chars means 10-99 GB, etc.

Читайте также:  Java executing linux command

Edit: here’s another version (since find . -printf «%Tc» crashes on MinGW/MSYS):

find . -type f -printf "%T@\t%p\n" | sort -n | cut -f 2- | xargs -I<> ls -Glath --si <> 
-rw-r--r-- 1 es 23K Jul 10 2010 ./laptop_0000071.jpg -rw-r--r-- 1 es 43M Jul 29 19:19 ./work.xcf -rw-r--r-- 1 es 87K Jul 29 20:11 ./patent_lamps/US Patent 274427 Maxim Lamp Holder.jpg -rw-r--r-- 1 es 151K Jul 29 20:12 ./patent_lamps/Edison screw-in socket.png -rw-r--r-- 1 es 50K Jul 29 20:13 ./patent_lamps/1157 Lamp.jpg -rw-r--r-- 1 es 38K Jul 29 20:14 ./patent_lamps/US06919684-20050719-D00001.png 
  • -I<> causes the occurrence of <> to be replaced by an argument, and newlines are now the argument separators (note the spaces in filenames above).
  • ls -G suppresses printing the group name (waste of space).
  • ls -h —si produces human-readable file sizes (more correct with —si ).
  • ls -t sorts by time, which is irrelevant here, but that’s what I typically use.

Источник

How to sort results of Find statement by date?

But ls -t is not working on find result but on whole directory edit: Result of this statement should be sorted by modification day directories. This regex suppose to match directories that contains only numbers and dots in name.

2 Answers 2

get file names only . sorted by modification day

find + sort + cut approach:

find . -regex ".*/[0-9.]+" -printf "%T@ %f\n" | sort | cut -d' ' -f2 
  • %T@ — File’s last modification time, where @ is seconds since Jan. 1, 1970, 00:00 GMT, with fractional part
  • %f — File’s name with any leading directories removed (only the last element)

To sort in descending order:

find . -regex ".*/[0-9.]+" -printf "%T@ %f\n" | sort -k1,1r | cut -d' ' -f2 

Your method can be adapted to work in simple cases. The main problem you’re facing is that you’re passing input to ls , but ls doesn’t take any input. ls takes command line arguments. So you need to pass the output of find as arguments to ls , with a command substitution. Also, in case a directory is matched, pass -d to ls to list the directory itself and not its contents.

OLDDATA=$(ls -td $(find . -regex ".*/[0-9.]+")) 

Only in simple cases, because there are two restrictions:

  1. This relies on an unquoted command substitution (and so does the use of $OLDDATA afterwards). Therefore it assumes that the file names don’t contain any special characters (whitespace or wildcard characters \[*? ).
  2. Some versions of ls may mangle characters that are not printable in the current locale.
  3. If the total length of the file names is too long, you’ll get an error. (Note that find … -exec and xargs cannot help here, since ls must run a single time to get the order of the file names right. All they could do is hide errors and produce output that is not correctly sorted — and mangle a few more characters, in the case of xargs .)
Читайте также:  Remove folder in linux with all files

A robust, simple way of doing this is to use zsh. It has the ability to sort wildcard matches, thanks to glob qualifiers.

setopt extended_glob OLDDATA=(**/[0-9.]##(om)) 
  • Since this doesn’t call any other program, there is no length limit other than available memory, and no risk of file name mangling at any point.
  • The result is a list of strings (each string being a file name), not a string, so it goes into an array variable.
  • **/ traverses subdirectories recursively, avoiding the use of find .
  • ## means “one or more of the preceding” in zsh extended glob syntax, it’s analogous to + in (extended) regex syntax.
  • (om) is a glob qualifier to sort files by modification time, like ls -t .

There is notoriously no simple way to do this robustly with POSIX tools or even with GNU tools and ksh.

Источник

Оцените статью
Adblock
detector