Differences between revisions 20 and 33 (spanning 13 versions)
Revision 20 as of 2013-10-27 03:22:05
Size: 2572
Editor: 123
Comment: Add another find method
Revision 33 as of 2022-10-31 16:05:02
Size: 2718
Editor: emanuele6
Comment: use --, UsingFind => [[UsingFind]]
Deletions are marked like this. Additions are marked like this.
Line 2: Line 2:
Line 5: Line 4:
{{{#!highlight bash
cd -- "$srcdir" &&
find . -type d -print | cpio -dumpv "$dstdir"
}}}
Line 6: Line 9:
{{{
 cd "$srcdir" &&
 find . -type d -print | cpio -pdumv "$dstdir"
or with the `pax` program:
{{{#!highlight bash
cd -- "$srcdir" &&
find . -type d -print | pax -rwdv  "$dstdir"
Line 10: Line 14:
or with the `pax` program:
Line 12: Line 15:
{{{
 cd "$srcdir" &&
 find . -type d -print | pax -rwdv "$dstdir"
}}}
or with zsh's special globbing:

{{{
 zsh -ec '
 cd -- "$srcdir"
 dirs=(**/*(/ND))
 cd -- "$dstdir"
 mkdir -p -- $dirs[@]'
}}}
Line 26: Line 16:

{{{
 cd "$srcdir" &&
 find . -type d -print | tar c --files-from - --no-recursion |
   tar x --directory "$dstdir"
{{{#!highlight bash
cd -- "$srcdir" &&
find . -type d -print | tar c --files-from - --no-recursion |
  tar x --directory "$dstdir"
Line 34: Line 23:
All but the zsh solution above will fail if directory names contain newline characters. On many modern BSD/GNU systems, at least, they can be trivially modified to cope with that, by using `find -print0` and one of `pax -0` or `cpio -0` or `tar --null` (check your system documentation to see which of these commands you have, and which extensions are available). All the solutions above will fail if directory names contain newline characters. On many modern BSD/GNU systems, at least, they can be trivially modified to cope with that, by using `find -print0` and one of `pax -0` or `cpio -0` or `tar --null` (check your system documentation to see which of these commands you have, and which extensions are available).  If you really don't have access to those options, you can probably, at least, use `! -path $'*\n*' -type d -print`, or better `-name $'*\n*' -prune -o -type d -print` (instead of `-type d -print`) to ignore directories that contain newline characters in their path.
Line 36: Line 25:
If you want to create stub files instead of full-sized files, with GNU [[UsingFind|find(1)]], the following is likely to be the simplest solution. The `find` command recreates the regular files using "dummy" files (empty files with the same timestamps): with find
{{{#!highlight bash
mkdir -p -- "$dstdir" &&
cd -- "$srcdir" &&
find . -type d -exec sh -c \
  'cd -- "$dstdir" && mkdir -- "$@"' sh {} +
}}}
Line 38: Line 33:
{{{
 cd "$srcdir" &&
 # use one of the above commands first, to make the directories, then:
 find . -type f -exec touch -r {} "$destination"/{} \;
or with bash 4's `globstar`
{{{#!highlight bash
shopt -s globstar nullglob &&
cd -- "$srcdir" && dirs=(**/) && (( ${#dirs[@]} )) &&
cd -- "$dstdir" && mkdir -- "${dirs[@]}"
Line 43: Line 39:
Be aware, though, that according to POSIX, the behaviour of `find` is unspecified when `{}` is not standing alone in an argument. Because of this, the following solution is more portable (and probably faster...) than the previous:
Line 45: Line 40:
{{{
 dstdir=whatever; export dstdir
 find . -type f -exec sh -c 'for i; do touch -r "$i" "$dstdir"/"$i"; done' _ {} +
or with zsh's special globbing:
{{{#!highlight bash
zsh -ec '
cd -- "$srcdir"
dirs=(**/*(/ND))
cd -- "$dstdir"
mkdir -- $dirs[@]'
Line 49: Line 48:
If your `find` can't handle `-exec +` then you can use `\;` instead of `+` at the end of the command. See UsingFind for explanations.
Line 51: Line 49:
Or use `find` with `xargs`: If you want to create stub files instead of full-sized files, the following is likely to be the simplest solution. The `find` command recreates the regular files using "dummy" files (empty files with the same timestamps):
{{{#!highlight bash
cd "$srcdir" &&
DSTDIR=$dstdir find . -type f -exec sh -c \
  'for i do touch -r "$i" -- "$DSTDIR/$i"; done' sh {} +
}}}
Line 53: Line 56:
{{{
find "$src" -type d | sed "s@$src@@g" | (cd "$dst" && xargs -n1 mkdir)
}}}
If your `find` can't handle `-exec +` then you can use `\;` instead of `+` at the end of the command. See [[UsingFind]] for explanations.

How can I recreate a directory hierarchy structure, without the files?

With the cpio program:

   1 cd -- "$srcdir" &&
   2 find . -type d -print | cpio -dumpv "$dstdir"

or with the pax program:

   1 cd -- "$srcdir" &&
   2 find . -type d -print | pax -rwdv  "$dstdir"

or with GNU tar, and more verbose syntax:

   1 cd -- "$srcdir" &&
   2 find . -type d -print | tar c --files-from - --no-recursion |
   3   tar x --directory "$dstdir"

This creates a list of directory names with find, non-recursively adds just the directories to an archive, and pipes it to a second tar instance to extract it at the target location. As you can see, tar is the least suited to this task, but people just adore it, so it has to be included here to appease the tar fanboy crowd. (Note: you can't even do this at all with a typical Unix tar. Also note: there is no such thing as "standard tar", as both tar and cpio were intentionally omitted from POSIX in favor of pax.)

All the solutions above will fail if directory names contain newline characters. On many modern BSD/GNU systems, at least, they can be trivially modified to cope with that, by using find -print0 and one of pax -0 or cpio -0 or tar --null (check your system documentation to see which of these commands you have, and which extensions are available). If you really don't have access to those options, you can probably, at least, use ! -path $'*\n*' -type d -print, or better -name $'*\n*' -prune -o -type d -print (instead of -type d -print) to ignore directories that contain newline characters in their path.

with find

   1 mkdir -p -- "$dstdir" &&
   2 cd -- "$srcdir" &&
   3 find . -type d -exec sh -c \
   4   'cd -- "$dstdir" && mkdir -- "$@"' sh {} +

or with bash 4's globstar

   1 shopt -s globstar nullglob &&
   2 cd -- "$srcdir" && dirs=(**/) && (( ${#dirs[@]} )) &&
   3 cd -- "$dstdir" && mkdir -- "${dirs[@]}"

or with zsh's special globbing:

   1 zsh -ec '
   2 cd -- "$srcdir"
   3 dirs=(**/*(/ND))
   4 cd -- "$dstdir"
   5 mkdir -- $dirs[@]'

If you want to create stub files instead of full-sized files, the following is likely to be the simplest solution. The find command recreates the regular files using "dummy" files (empty files with the same timestamps):

   1 cd "$srcdir" &&
   2 DSTDIR=$dstdir find . -type f -exec sh -c \
   3   'for i do touch -r "$i" -- "$DSTDIR/$i"; done' sh {} +

If your find can't handle -exec + then you can use \; instead of + at the end of the command. See UsingFind for explanations.


CategoryShell

BashFAQ/010 (last edited 2023-09-22 06:29:48 by StephaneChazelas)