73

On Linux, I have a --parents option available for the cp command so I can do

cp --parents test/withintest/go.rb test2

http://www.gnu.org/software/coreutils/manual/html_node/cp-invocation.html

On Mac, I do not have this option available. Is there a way to do this on Mac? Why is this option not available?

PS. The purpose of --parents is the following:

‘--parents’ Form the name of each destination file by appending to the target directory a slash and the specified name of the source file.

The last argument given to cp must be the name of an existing directory.

For example, the command:

      cp --parents a/b/c existing_dir 

copies the file a/b/c to existing_dir/a/b/c, creating any missing intermediate directories.

Laurent Parenteau
  • 2,516
  • 20
  • 31
dmonopoly
  • 3,251
  • 5
  • 34
  • 49
  • 4
    Just install it :) As with many GNU utilities, you can install them from a package manager, e.g. `brew install coreutils`. – flow2k Nov 16 '20 at 21:52
  • 2
    For reference, after install coreutils above, you can then run `gcp --parents a/b/c existing_dir` since coreutils prepends a `g` to GNU commands that MacOS has its own version of. – Chris Hayes Nov 12 '21 at 17:26

8 Answers8

99

This bothered me quite a lot as well. A workaround for this could be to use rsync.

rsync -R test/withintest/go.rb test2

has the same effect as cp --parents and OS X comes standard with rsync.

Community
  • 1
  • 1
Stephan
  • 1,505
  • 1
  • 11
  • 19
  • 3
    do you know why this might be failing with `find` and `xargs`? I tried `find . -name "foo" | xargs rsync -R '{}' destination/` – AutonomousApps Oct 05 '20 at 23:44
19

You can use the ditto command on Mac OS X:

The basic form

ditto <src-path> <dst-path>

does what you want. There's a lot more options too - check out the man page.

Kara
  • 6,115
  • 16
  • 50
  • 57
Echelon
  • 7,306
  • 1
  • 36
  • 34
  • Perfect. `ditto some/dir/structure/file /tmp/some/dir/structure/file` creates the directories some/dir/structure under /tmp, as desired. – ca2longoria Jul 22 '15 at 16:13
  • 20
    I don't see how this answers the original question. `cp --parents a/b/c existing_dir` and `ditto a/b/c existing_dir` seem to do different things. – alecbz Dec 24 '15 at 05:19
  • 1
    @alecb I think the answer was addressing what the OP actually wanted to achieve, rather than the exact method. I was going through the same thought process when I discovered the `ditto` command. – Echelon Dec 28 '15 at 11:55
  • 5
    Original question is to copy a *file* while preserving it's path hierarchy. ditto copies over a whole dir tree - not a single file -- therefore, wrong answer. – Moos Dec 04 '17 at 21:03
  • 1
    That's wrong to use `ditto` when there is cross-platform `rsync` available. The "ditto" answer can't be designated as a correct answer. Sorry for being this nasty. – KostaZ Dec 22 '19 at 15:22
  • @KostaZ misfire. The title says `mac` so this answer is viable. – WestCoastProjects Nov 14 '22 at 05:27
6

You can install the GNU version of cp using MacPorts.

After MacPorts is installed you can install the coreutils packages:

sudo port install coreutils

Then you will be able to use the GNU version cp and other core utilitites (ls, date, cat, etc.) by prefixing the command with a g:

gcp --parents test/withintest/go.rb test2

If you want these GNU versions to be used by default you can add the GNU bin update your path. Add the following to your ~/.bash_profile:

export PATH="/opt/local/libexec/gnubin:$PATH"
Kara
  • 6,115
  • 16
  • 50
  • 57
4

The Homebrew way:

Install coreutils

brew install coreutils

Use the GNU g- prefixed command

gcp --parents test/withintest/go.rb test2
Martin Peter
  • 3,565
  • 2
  • 23
  • 26
4

I used rsync and what I did was:

rsync -R dir/**/file.json destination

3

Try

mkdir -p `dirname "$file_path"` && cp "$old_dir/$file_path" "$file_path"

This first creates the directory with all itermediates in the relative file path. Then it copies the file to the newly created directory.

Edward Ji
  • 745
  • 8
  • 19
0

I would not replace mac cp with GNU cp. I would also not used ditto because it is not cross-platform. Instead use cross-platform tools, such as rsync:

rsync <srcDir/srcFile> <dst>

Result: dst/srcDir/srcFile

vikrantt
  • 2,537
  • 1
  • 13
  • 7
  • this one didn't quite work for me, `rsync old/qtbase/src/plugins/platforms/xcb/xcb_qpa_lib.pro new` gives me `new/xcb_qpa_lib.pro` on a mac – tofutim Mar 18 '18 at 02:09
  • 1
    See stephan's answer below, similar but it works on mac. – jaime May 07 '18 at 02:11
0

I had to add the -dir option to rsync to get it to work for me:

rsync -R -dir a/b tmp

The results:

$ find a
a
a/b
a/b/f1.txt
a/c
a/c/f2.txt
a/d
a/d/f3.txt

$ find tmp
tmp
tmp/a
tmp/a/b
tmp/a/b/f1.txt