cp --no-target-directory explained

23,478

Solution 1

By default, cp tests if its last argument is an existing directory. If this happens, cp creates a link inside that directory, with the base name of the source. That is, given the command

cp foo/bar wibble

if wibble is an existing directory then cp copies the source to wibble/bar. If wibble does not exist then cp links the source to wibble.

If you want to be sure that the copy is always wibble, then you can specify the --no-target-directory (alias -T) option. That way, if cp succeeds, you can be sure that the copy is called wibble. If wibble already existed as a directory, then cp will fail.

In tabular form:

The target is …             Without -T               With -T
existing directory          copy in the directory    error
existing file (not dir)     overwrite                overwrite
does not exist              create                   create

The only difference is that with -T, in case the target is an existing directory, the command returns an error. This is useful when you expect the directory not to exist: you get an error message instead of something unpredicted happening.

The same applies to mv and ln. If the target is an existing directory, with -T, they signal an error rather than silently doing something different.

With cp, there's a different case. If you do a recursive copy and the source is a directory, then cp -T copies the content of the source into the destination, rather than copying the source itself. That is, given

$ tree source destination 
source
└── foo
destination
└── bar

then

$ cp -rv source destination
`source' -> `destination/source'
`source/foo' -> `destination/source/foo'

whereas

% cp -rvT source destination
`source/foo' -> `destination/foo'

Solution 2

You would use --no-target-directory if you don't want a source directory copied underneath an existing destination directory, you want the source directory copied onto the destination directory.

Here is an example of a directory copy with and without --no-target-directory:

$ mkdir a
$ touch a/b a/c
$ find
.
./a
./a/c
./a/b
$ cp -r a b       # b does not exist; becomes copy of a
$ find
.
./b
./b/b
./b/c
./a
./a/c
./a/b
$ rm -r b
$ mkdir b
$ cp -r a b       # b already exists; a is copied *underneath* it
$ find
.
./b
./b/a
./b/a/b
./b/a/c
./a
./a/c
./a/b
$ rm -r b
$ mkdir b
$ cp -r --no-target-directory a b     # b already exists; becomes copy of a
$ find
.
./b
./b/b
./b/c
./a
./a/c
./a/b

You can achieve something of the same effect by suffixing the source directory name(s) with slash-dot /. as in: cp -r a/. b which copies source directory a onto b and not underneath b.

Neither of the above methods are the same as saying "copy only the contents of the source directory to the existing destination" since, if you ask to preserve time and permissions, the existing destination directory will acquire the time and permissions of the source directory. An example (edited to remove unnecessary information):

$ find . -ls
drwx------   Oct 13 13:31 ./b         # note date and permissions
drwxr-xr-x   Jan  1  2013 ./a         # note date and permissions
-rw-r--r--   Oct 13 13:23 ./a/c
-rw-r--r--   Oct 13 13:23 ./a/b
$ cp -rp --no-target-directory a b    # preserve mode and timestamps
$ find . -ls
drwxr-xr-x   Jan  1  2013 ./b         # note copied date and permissions
-rw-r--r--   Oct 13 13:23 ./b/b
-rw-r--r--   Oct 13 13:23 ./b/c
drwxr-xr-x   Jan  1  2013 ./a
-rw-r--r--   Oct 13 13:23 ./a/c
-rw-r--r--   Oct 13 13:23 ./a/b

A content-only copy would not transfer the mode or timestamps of the source directory to the destination directory.

Solution 3

How about the following?

$ cp -rvT Dir_1 Dir_2
‘Dir_1/File_3.txt’ -> ‘Dir_2/File_3.txt’
‘Dir_1/File_1.txt’ -> ‘Dir_2/File_1.txt’
‘Dir_1/File_2.txt’ -> ‘Dir_2/File_2.txt’
$ cp -rv Dir_1 Dir_2
‘Dir_1’ -> ‘Dir_2/Dir_1’
‘Dir_1/File_3.txt’ -> ‘Dir_2/Dir_1/File_3.txt’
‘Dir_1/File_1.txt’ -> ‘Dir_2/Dir_1/File_1.txt’
‘Dir_1/File_2.txt’ -> ‘Dir_2/Dir_1/File_2.txt’

As such, it's just a different way of writing cp Dir_1/* Dir_2/. However, it does catch hidden files at the root of Dir_1 that would be missed by a simple cp *.

$ touch Dir_1/.Hidden_File_{1,2,3}.txt
$ cp -rv Dir_1/* Dir_2
cp: No match.
$ cp -rvT Dir_1 Dir_2
‘Dir_1/.Hidden_File_2.txt’ -> ‘Dir_2/.Hidden_File_2.txt’
‘Dir_1/.Hidden_File_3.txt’ -> ‘Dir_2/.Hidden_File_3.txt’
‘Dir_1/.Hidden_File_1.txt’ -> ‘Dir_2/.Hidden_File_1.txt’
Share:
23,478

Related videos on Youtube

erch
Author by

erch

my about me is blink at the moment

Updated on September 18, 2022

Comments

  • erch
    erch almost 2 years

    Question: I need a simple example of how to use cp --no-target-directory.

    I do experience some difficulties in understanding cp --no-target-directory. I do understand the explanation for mv --no-target-directory, but I can't really imagine a way to use it for cp.

    For example, when the command mv /tmp/source /tmp/dest succeeds, there is no guarantee that /tmp/source was renamed to /tmp/dest: it could have been renamed to /tmp/dest/source instead, if some other process created /tmp/dest as a directory. However, ifmv -T /tmp/source /tmp/dest succeeds, there is no question that /tmp/source was renamed to/tmp/dest`. (source)

  • erch
    erch over 10 years
    works for me with[out] --no-target-directory option: as long as I use --recursive, everything is fine [with coreutils 8.12 under GNU/Linux]. The main difference seems to be that with --no-target-directory the content but not the directory itself is copied [research still in progress]