0

This question has parallels to question "touch all folders in a directory".

How to touch everything in a directory,

  • recursively
  • including hidden entries, like "directory up" .. and .
  • without de-referencing symbolic links touch -h and
  • use a reference file touch -r <file> as the the time stamp source

    from within a shell-script?

3

If your touch command supports -h for no dereference:

find . -depth -exec touch -h -r "$reference_file" {} +
touch -c -h -r "$reference_file" ..

(note that -h implies -c (to avoid creating the file if it didn't exist with NetBSD/FreeBSD, but not with GNU or busybox touch (though with GNU touch it wouldn't create files either and print an error message), so adding -c here for increased portability).

Or with with a single find command which could reduce the number of touch commands being run:

find .. . -depth \( ! -name .. -o -prune \) -exec touch -c -h -r "$reference_file" {} +

That is, add .. to the list of files given to find, but tell find to prune it (not descend into it).

For an arbitrary directory (well one whose path doesn't start with -:

find "$dir/.." "$dir/" \( ! -name .. -o -prune \) \
  -exec touch -c -h -r "$reference_file" {} +

(here using $dir/ instead of $dir for the case where $dir refers to a symlink to a directory).

With BSD find, you can use

find -f "$dir/.." -f "$dir/" \( ! -name .. -o -prune \) \
  -exec touch -c -h -r "$reference_file" -- {} +

to avoid the problems with $dir starting with -.

Though you might as well do:

(
 cd -P -- "$dir/" &&
   exec find .. . \( ! -name .. -o -prune \) \
     -exec touch -c -h -r "$reference_file" {} +
)

(assuming $reference_file is not a relative path).

Note that if $reference_file is a symlink, with GNU touch and with -h, the modification time of the symlink will be used (that of the target would be used without -h) while with NetBSD (where -h comes from) and FreeBSD touch, the modification time of the target is used with or without -h.

If using zsh, you could use its recursive globbing

autoload zargs
zargs -- $dir/{.,..,**/*(NDoN)} -- touch -c -h -r $reference_file --

(the oN for not sorting the list can be omitted, that's just for optimisation).

ksh93 eventually added support for zsh's recursive globbing in 2005, with the globstar option.

(set -o globstar; FIGNORE=
 command -x touch -c -h -r "$reference_file" -- ~(N)"$dir"/**)

Note however that ksh will include all . and .. entries here so all directories will be touched several times.

bash eventually copied ksh93's globstar in 2009, but was initially /broken/ in that it was following symlinks when descending directories. It was fixed in 4.3 in 2014.

bash doesn't have the equivalent of zsh's zargs or ksh93's command -x to split command lines so as to avoid the arg list too long issues. On a GNU system, you could always use GNU xargs for that:

xargs -r0a <(
  shopt -s dotglob nullglob globstar
  printf '%s\0' "$dir/"{.,..,**}
) touch -c -h -r "$reference_file" --

Now, I would probably still use find here. Beside the poorer performance, another issue with globs is that errors (like access denied) when traversing the directory tree are silently ignored.

| improve this answer | |
  • @ProBackup, see edit – Stéphane Chazelas Dec 10 '17 at 21:58
  • With arch-linux bash this is the input: find "/tmp/drp.DSK03fCQTKFZMND/rootfs/.." "/tmp/drp.DSK03fCQTKFZMND/rootfs/" \( ! name .. -o -prune \) -exec touch -h -r /boot/vmlinuz-linux {} + and its output find: paths must precede expression: name Usage: find [-H] [-L] [-P] [-Olevel] [-D help|tree|search|stat|rates|opt|exec] [path...] [expression] – Pro Backup Dec 10 '17 at 22:00
  • 1
    @ProBackup, it should be -name, not name. – Stéphane Chazelas Dec 10 '17 at 22:02
  • @PesaThe, see edit. – Stéphane Chazelas Dec 11 '17 at 9:47

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.