Looking for a command that will return the single most recent file in a directory.
Not seeing a limit parameter to ls
...
This question is related to
linux
shell
command-line
ls -Frt | grep "[^/]$" | tail -n 1
try this simple command
ls -ltq <path> | head -n 1
If you want file name - last modified, path = /ab/cd/*.log
If you want directory name - last modified, path = /ab/cd/*/
A note about reliability:
Since the newline character is as valid as any in a file name, any solution that relies on lines like the head
/tail
based ones are flawed.
With GNU ls
, another option is to use the --quoting-style=shell-always
option and a bash
array:
eval "files=($(ls -t --quoting-style=shell-always))"
((${#files[@]} > 0)) && printf '%s\n' "${files[0]}"
(add the -A
option to ls
if you also want to consider hidden files).
If you want to limit to regular files (disregard directories, fifos, devices, symlinks, sockets...), you'd need to resort to GNU find
.
With bash 4.4 or newer (for readarray -d
) and GNU coreutils 8.25 or newer (for cut -z
):
readarray -t -d '' files < <(
LC_ALL=C find . -maxdepth 1 -type f ! -name '.*' -printf '%T@/%f\0' |
sort -rzn | cut -zd/ -f2)
((${#files[@]} > 0)) && printf '%s\n' "${files[0]}"
Or recursively:
readarray -t -d '' files < <(
LC_ALL=C find . -name . -o -name '.*' -prune -o -type f -printf '%T@%p\0' |
sort -rzn | cut -zd/ -f2-)
Best here would be to use zsh
and its glob qualifiers instead of bash
to avoid all this hassle:
Newest regular file in the current directory:
printf '%s\n' *(.om[1])
Including hidden ones:
printf '%s\n' *(D.om[1])
Second newest:
printf '%s\n' *(.om[2])
Check file age after symlink resolution:
printf '%s\n' *(-.om[1])
Recursively:
printf '%s\n' **/*(.om[1])
Also, with the completion system (compinit
and co) enabled, Ctrl+Xm becomes a completer that expands to the newest file.
So:
vi Ctrl+Xm
Would make you edit the newest file (you also get a chance to see which it before you press Return).
vi Alt+2Ctrl+Xm
For the second-newest file.
vi *.cCtrl+Xm
for the newest c
file.
vi *(.)Ctrl+Xm
for the newest regular file (not directory, nor fifo/device...), and so on.
using R recursive option .. you may consider this as enhancement for good answers here
ls -arRtlh | tail -50
Shorted variant based on dmckee's answer:
ls -t | head -1
ls -t -1 | sed '1q'
Will show the last modified item in the folder. Pair with grep
to find latest entries with keywords
ls -t -1 | grep foo | sed '1q'
With only Bash builtins, closely following BashFAQ/003:
shopt -s nullglob
for f in * .*; do
[[ -d $f ]] && continue
[[ $f -nt $latest ]] && latest=$f
done
printf '%s\n' "$latest"
I needed to do it too, and I found these commands. these work for me:
If you want last file by its date of creation in folder(access time) :
ls -Aru | tail -n 1
And if you want last file that has changes in its content (modify time) :
ls -Art | tail -n 1
Presuming you don't care about hidden files that start with a .
ls -rt | tail -n 1
Otherwise
ls -Art | tail -n 1
Recursively:
find $1 -type f -exec stat --format '%Y :%y %n' "{}" \; | sort -nr | cut -d: -f2- | head
I use:
ls -ABrt1 --group-directories-first | tail -n1
It gives me just the file name, excluding folders.
ls -lAtr | tail -1
The other solutions do not include files that start with '.'
.
This command will also include '.'
and '..'
, which may or may not be what you want:
ls -latr | tail -1
This is a recursive version (i.e. it finds the most recently updated file in a certain directory or any of its subdirectory)
find $DIR -type f -printf "%T@ %p\n" | sort -n | cut -d' ' -f 2- | tail -n 1
Edit: use -f 2-
instead of -f 2
as suggested by Kevin
If you want to get the most recent changed file also including any subdirectories you can do it with this little oneliner:
find . -type f -exec stat -c '%Y %n' {} \; | sort -nr | awk -v var="1" 'NR==1,NR==var {print $0}' | while read t f; do d=$(date -d @$t "+%b %d %T %Y"); echo "$d -- $f"; done
If you want to do the same not for changed files, but for accessed files you simple have to change the
%Y parameter from the stat command to %X. And your command for most recent accessed files looks like this:
find . -type f -exec stat -c '%X %n' {} \; | sort -nr | awk -v var="1" 'NR==1,NR==var {print $0}' | while read t f; do d=$(date -d @$t "+%b %d %T %Y"); echo "$d -- $f"; done
For both commands you also can change the var="1" parameter if you want to list more than just one file.
Finding the most current file in every directory according to a pattern, e.g. the sub directories of the working directory that have name ending with "tmp" (case insensitive):
find . -iname \*tmp -type d -exec sh -c "ls -lArt {} | tail -n 1" \;
ls -t | head -n1
This command actually gives the latest modified file in the current working directory.
All those ls/tail solutions work perfectly fine for files in a directory - ignoring subdirectories.
In order to include all files in your search (recursively), find can be used. gioele suggested sorting the formatted find output. But be careful with whitespaces (his suggestion doesn't work with whitespaces).
This should work with all file names:
find $DIR -type f -printf "%T@ %p\n" | sort -n | sed -r 's/^[0-9.]+\s+//' | tail -n 1 | xargs -I{} ls -l "{}"
This sorts by mtime, see man find:
%Ak File's last access time in the format specified by k, which is either `@' or a directive for the C `strftime' function. The possible values for k are listed below; some of them might not be available on all systems, due to differences in `strftime' between systems.
@ seconds since Jan. 1, 1970, 00:00 GMT, with fractional part.
%Ck File's last status change time in the format specified by k, which is the same as for %A.
%Tk File's last modification time in the format specified by k, which is the same as for %A.
So just replace %T
with %C
to sort by ctime.
I personally prefer to use as few not built-in bash
commands as I can (to reduce the number of expensive fork and exec syscalls). To sort by date the ls
needed to be called. But using of head
is not really necessary. I use the following one-liner (works only on systems supporting name pipes):
read newest < <(ls -t *.log)
or to get the name of the oldest file
read oldest < <(ls -rt *.log)
(Mind the space between the two '<' marks!)
If the hidden files are also needed -A arg could be added.
I hope this could help.
I like echo *(om[1])
(zsh
syntax) as that just gives the file name and doesn't invoke any other command.
The find / sort solution works great until the number of files gets really large (like an entire file system). Use awk instead to just keep track of the most recent file:
find $DIR -type f -printf "%T@ %p\n" |
awk '
BEGIN { recent = 0; file = "" }
{
if ($1 > recent)
{
recent = $1;
file = $0;
}
}
END { print file; }' |
sed 's/^[0-9]*\.[0-9]* //'
Source: Stackoverflow.com