Differences between revisions 1 and 12 (spanning 11 versions)
Revision 1 as of 2009-03-18 05:20:23
Size: 541
Editor: localhost
Comment: created by Samus_
Revision 12 as of 2010-11-10 23:49:51
Size: 2762
Editor: pool101
Comment: fixed doc 20101109
Deletions are marked like this. Additions are marked like this.
Line 1: Line 1:
Describe BashFAQ/099 here.
Line 4: Line 2:
== How can I get the newest file from a directory? ==
Parsing the output of 'ls' is a bad practice, you should create a loop and compare the timestamps:
== How can I get the newest (or oldest) file from a directory? ==
The intuitive answer of `ls -t | head -1` is wrong, because parsing the output of `ls` is [[ParsingLs|unsafe]]; instead, you should create a loop and compare the timestamps:
Line 8: Line 6:
saved_ts=0
for f in *; do
  f_ts=$(stat --format=%Y "$f")

  if ((f_ts > newest_ts)); then
    newest_ts=$f_ts
    newest_f=$f
# Bash
files=(*) newest=${files[0]}
for f in "${files[@]}"; do
  if [[ $f -nt $newest ]]; then
    newest=$f
Line 19: Line 15:
there you'll have the newest file at $newest_f, notice that when a file changes also changes its timestamp so this will get the last created or modified file in the directory. Then you'll have the newest file (according to modification time) in `$newest`. To get the oldest, simply change `-nt` to `-ot` (see `help test` for a list of operators), and of course change the names of the variables to avoid confusion.

Bash has no means of comparing file timestamps other than `mtime`, so if you wanted to get (for example) the most-recently-accessed file (newest by `atime`), you would have to get some help from the external command `stat(1)` (if you have it) or the [[BashLoadable|loadable builtin]] `finfo` (if you can load builtins).

Here's an example using `stat` from GNU coreutils 6.10 (sadly, even across Linux systems, the syntax of `stat` is not consistent) to get the most-recently-accessed file. (In this version, `%X` is the last access time.)

{{{
# Bash, GNU coreutils
newest= newest_t=0
for f in *; do
  t=$(stat --format=%X -- "$f") # atime
  if ((t > newest_t)); then
    newest_t=$t
    newest=$f
  fi
done
}}}

This also has the disadvantage of spawning an external command for every file in the directory, so it should be done this way only if necessary. To get the oldest file using this technique, you'd either have to initialize `oldest_t` with the largest possible timestamp (a tricky proposition, especially as we approach the year 2038), or with the timestamp of the first file in the directory, as we did in the first example.

Here is another solution spawning an external command, but posix:
{{{
# posix
unset newest
for f in ./*; do
  # set the newest during the first iteration
  newest=${newest-$f}
  #-prune to avoid descending in the directories, the exit status of find is useless here, we check the output
  if [ "$(find "$f" -prune -newer "$newest")" ]; then
    newest=$f
  fi
done
}}}



 How to remove all but the most recent directory.
{{{
 $cat clean-old
 dirs=(enginecrap/*/) newest=${dirs[0]}
 for d in "${dirs[@]}"
  do if [[ $d -nt $newest ]]
     then newest=$d
     fi
  done

 for z in "${dirs[@]}"
  do if [[ "$z" != "$newest" ]]
     then rm -rf "$z"
     fi
  done
 $ for x in 20101022 20101023 200101025 20101107 20101109;do mkdir enginecrap/"$x";done
 $ ls enginecrap/
 200101025 20101022 20101023 20101107 20101109
 $ ./clean-old
 $ ls enginecrap/
 20101109
}}}


----
CategoryShell

How can I get the newest (or oldest) file from a directory?

The intuitive answer of ls -t | head -1 is wrong, because parsing the output of ls is unsafe; instead, you should create a loop and compare the timestamps:

# Bash
files=(*) newest=${files[0]}
for f in "${files[@]}"; do
  if [[ $f -nt $newest ]]; then
    newest=$f
  fi
done

Then you'll have the newest file (according to modification time) in $newest. To get the oldest, simply change -nt to -ot (see help test for a list of operators), and of course change the names of the variables to avoid confusion.

Bash has no means of comparing file timestamps other than mtime, so if you wanted to get (for example) the most-recently-accessed file (newest by atime), you would have to get some help from the external command stat(1) (if you have it) or the loadable builtin finfo (if you can load builtins).

Here's an example using stat from GNU coreutils 6.10 (sadly, even across Linux systems, the syntax of stat is not consistent) to get the most-recently-accessed file. (In this version, %X is the last access time.)

# Bash, GNU coreutils
newest= newest_t=0
for f in *; do
  t=$(stat --format=%X -- "$f")   # atime
  if ((t > newest_t)); then
    newest_t=$t
    newest=$f
  fi
done

This also has the disadvantage of spawning an external command for every file in the directory, so it should be done this way only if necessary. To get the oldest file using this technique, you'd either have to initialize oldest_t with the largest possible timestamp (a tricky proposition, especially as we approach the year 2038), or with the timestamp of the first file in the directory, as we did in the first example.

Here is another solution spawning an external command, but posix:

# posix
unset newest
for f in ./*; do
  # set the newest during the first iteration
  newest=${newest-$f} 
  #-prune to avoid descending in the directories, the exit status of find is useless here, we check the output
  if [ "$(find "$f" -prune -newer "$newest")" ]; then 
    newest=$f
  fi
done
  • How to remove all but the most recent directory.

 $cat clean-old 
 dirs=(enginecrap/*/) newest=${dirs[0]}
 for d in "${dirs[@]}"
  do if [[ $d -nt $newest ]]
     then newest=$d
     fi
  done

 for z in "${dirs[@]}"
  do if [[ "$z" != "$newest" ]]
     then rm -rf "$z"
     fi
  done
 $ for x in  20101022 20101023 200101025 20101107 20101109;do mkdir enginecrap/"$x";done
 $ ls enginecrap/
 200101025       20101022        20101023        20101107        20101109
 $ ./clean-old 
 $ ls enginecrap/
 20101109


CategoryShell

BashFAQ/099 (last edited 2016-03-23 09:25:56 by geirha)