POSIXly, you'd use pax
in read+write mode with the -l
option:
pax -rwlpe -s /A/B/ dirA .
(-pe
preserves all possible attributes of files (in this case only directories) that are copied, like GNU cp
's -a
does).
Now, though standard, that command is not necessarily very portable.
First, many GNU/Linux-based systems don't include pax
by default (even though that's a non-optional POSIX utility).
Then, a number of bugs and non-conformances with a few implementations cause a number of issues with that code.
- because of a bug, Solaris 10
pax
(at least) doesn't work when using -rwl
in combination with -s
. For some reason, it seems it applies the substitution to both the original and copied path. So above, it would attempt to do some link("dirB/file", "dirB/file")
instead of link("dirA/file", "dirB/file")
.
- on FreeBSD,
pax
doesn't create hardlinks for files of type symlink (a behaviour allowed by POSIX). Not only that, but it also applies the substitution to the targets of the symlinks (a behaviour not allowed by POSIX). So for instance if there's a foo -> AA
symlink in dirA
, it will become foo -> BA
in dirB
.
Also, if you want to do the same but with arbitrary file paths whose content is stored in $src
and $dst
, it's important to realise that pax -rwl -- "$src" "$dst"
creates the full directory structure of $src
inside $dst
(that has to exist and be a directory). For instance, if $src
is foo/bar
, then, $dst/foo/bar
is created.
If instead, you want $dst
to be a copy of $src
, the easiest is probably to do it as:
absolute_dst=$(umask 077 && mkdir -p -- "$dst" && cd -P -- "$dst" && pwd -P) &&
(cd -P -- "$src" && pax -rwlpe . "$absolute_dst")
(which would also work around most of the problems mentioned above but would fail if the absolute path of $dst
ends in newline characters).
Now that won't help on GNU/Linux systems where there's no pax
.
It's interesting to note that pax
was created by POSIX to merge the features of the tar
and cpio
commands.
cpio
is a historical Unix command (from 1977) as opposed to a POSIX invention, and there is a GNU implementation as well (not a pax
one). So even though it is no longer a standard command (it was in SUSv2 though), it is still very common, and there's a core set of features you can usually rely on.
The equivalent of pax -rwl
would be cpio -pl
. However:
cpio
takes the list of input file on stdin as opposed to arguments (newline delimited which means file names with newline characters are not supported)
- All files have to be specified (typically you feed it the output of
find
(find
and cpio
were developed jointly by the same people)).
- metadata are not preserved (some
cpio
implementations have options to preserve some, but nothing portable).
So with cpio
:
absolute_dst=$(umask 077 && mkdir -p -- "$dst" && cd -P -- "$dst" && pwd -P) &&
(cd -P -- "$src" && find . | cpio -pl "$absolute_dst")
pax
, on FreeBSD,cp -a
doesn't hardlink symlinks. – Stéphane Chazelas May 11 '15 at 20:05-a
=--archive
= "same as-dR --preserve=all
" = "never follow symbolic links in SOURCE",--preserve=links
, and "copy directories recursively", while-l
means "hard link files instead of copying" – endolith Jan 20 '21 at 16:14--parents
. Useful when you want to back up some files deep down into the file tree, and want to recover by simply copy paste everything – WesternGun Jan 30 '24 at 12:37