0

I would like to modify the date of a file file in order for it to be older than the ones present in the directory dir. The value of the time difference doesn't matter, as the goal is only to make it older in order to make it older.

In order to do this, I need to touch file with a date value older than the ones present in dir. How can I recover the date value of the oldest file in dir and subtract from it a certain amount of time (e.g. 1 second)?

2
  • use stat to get the mtime of the files in the current directory; sort to find the smallest mtime value, then shell arithmetic to subtract 1. Use date to convert the unix time value to a format suitable for touch Jul 17, 2019 at 15:55
  • Are there subdirectories of dir to consider, or only the files directly in that directory?
    – Jeff Schaller
    Jul 17, 2019 at 22:05

4 Answers 4

2

You could get the timestamps with find's -printf or with stat and sort them to get the oldest. Then subtract what you want and use it as the date specification for touch. find has the disadvantage of printing fractional seconds which have to be removed for the calculation.

oldest=$(stat -c "%Y" dir/*|sort|head -1)
touch -d "@$((oldest-1))" dir/file
# or touch -d "@$((oldest-60))" file # subtract 1 min to see the difference in normal ls -l output.

The date syntax -d @seconds-since-epoch is supported by GNU touch. It is not specified by POSIX.

The stat command is not specified by POSIX, it is part of GNU coreutils, see https://stackoverflow.com/questions/27828585/posix-analog-of-coreutils-stat-command.

So this solution should work on Linux systems, but probably not on general UNIX systems.

1
  • To my knowledge, stat is not specified by POSIX; it's common on many Linux systems. (I didn't see an OS tag on the question, so I thought I'd clarify the restrictions on the answer)
    – Jeff Schaller
    Jul 17, 2019 at 22:07
1

You can do this with a two-step process: copy the timestamp then adjust it to be older:

#find the eldest file in dir
eldest=$(ls -t dir | tail -1)

#duplicate the time
touch -r "dir/$eldest" myfile

#make the file one second older
touch -A -000001 myfile
5
  • My Debian 9 system doesn't support -A but you can use -t use [[CC]YY]MMDDhhmm[.ss] instead of current time Jul 17, 2019 at 16:05
  • This will fail if the oldest file ever contains a newline -- ls will dutifully print the filename on two lines, then tail will pick up the fragment.
    – Jeff Schaller
    Jul 17, 2019 at 22:03
  • The -A is BSD only, doesn't work in Linux.
    – user232326
    Jul 17, 2019 at 22:30
  • A ls -t dir will include any subdir in the directory.
    – user232326
    Jul 17, 2019 at 23:03
  • @Isaac that's right, as intended, per OP spec. But also trivial enough to alter to handle other spec, like "plain file only" or whatever. The crux of the issue is the timestamp adjustment. Jul 17, 2019 at 23:07
0

Portably, you'd have to use perl to gather the timestamps, subtract the amount of time you'd like, then format it for touch.

t=$(perl make-oldest 1 dir/*)
if [ "$t" -gt 0 ]
then
  touch -t "$t" file
else
  echo "Sorry, unable to find a file!"
fi

... and the make-oldest perl script is:

#!/usr/bin/perl -w
use strict;
use POSIX qw(strftime);

my $subtract = shift;
my $oldest = 0;
for (@ARGV) {
  my @s = stat;
  next unless @s;
  if ($oldest) {
    $oldest = $s[9] if $s[9] < $oldest;
  } else {
    $oldest = $s[9];
  }
}

if ($oldest) {
  # convert ($oldest - $subtract) into CCCCYYMMDDhhmm.SS
  print strftime "%Y%m%d%H%M.%S\n", localtime($oldest - $subtract);
} else {
  print "0\n";
}

The intention is that your shell expands the wildcard dir/* to get the list of filenames. There are "two" arguments to the perl script: the number of seconds to subtract from the oldest file, and the list of files to gather timestamps from.

The perl script pulls off the subtraction argument then loops over the given files and keeps track of the oldest modification time. If it cannot read any files, then it will return zero (as tested by the wrapper script, above). If an oldest file was found, then we use the strftime function to convert the subtracted timestamp into the appropriate format for touch.

0

A three step solution is needed:

  1. extract oldest timestamp (of only files, not dirs):

    $ oldestfile=$(find path/to/dir/ -maxdepth 1 -type f -printf '%A@ %f\n' | sort -rn | head -n 1)
    
  2. extract the file name (remove timestamp):

    $ oldestfile="${oldestfile#* }"
    
  3. touch (GNU) the file to the correct time:

    $ touch -r "$oldestfile" -d '-1 min' "file"
    

That will generate a file named file (if it doesn't exist) 1 minute older than the oldest file (excluding dirs) in path/to/dir/.

You must log in to answer this question.

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