28

Why do some GNU Coreutils commands have the -T/--no-target-directory option? It seems like everything that it does can be achieved using the semantics of the . (self dot) in a traditional Unix directory hierarchy.

Considering:

cp -rT /this/source dir

The -T option prevents the copy from creating a dir/source subdirectory. Rather /this/source is identified with dir and the contents are mapped between the trees accordingly. So for instance /this/source/foo.c goes to dir/foo.c and so on, rather than to dir/source/foo.c.

But this can be easily accomplished without the -T option using:

cp -r /this/source/. dir  # Probably worked fine since dawn of Unix?

Semantically, the trailing dot component is copied as a child of dir, but of course that "child" already exists (so doesn't have to be created) and is actually dir itself, so the effect is that /this/path is identified with dir.

It works fine if the current directory is the target:

cp -r /this/tree/node/. . # node's children go to current dir

Is there something you can do only with -T that can rationalize its existence? (Besides support for operating systems that don't implement the dot directory, a rationale not mentioned in the documentation.)

Does the above dot trick not solve the same race conditions that are mentioned in the GNU Info documentation about -T?

4 Answers 4

33

Your . trick can only be used when you're copying a directory, not a file. The -T option works with both directories and files. If you do:

cp srcfile destfile

and there's already a directory named destfile it will copy to destfile/srcfile, which may not be intended. So you use

cp -T srcfile destfile

and you correctly get the error:

cp: cannot overwrite directory `destfile' with non-directory

If you tried using the . method, the copy would never work:

cp: cannot stat `srcfile/.`: Not a directory
2
  • The . trick does work when copying a file, just not when renaming its basename at the same time! cp /path/to/file /target/dir/. If /target/dir/file exists and is a directory, you get the same diagnostic! But you have shown what -T does that can't be done without it in one step, without race conditions: copy a file and change its name without it being shunted to a subdirectory.
    – Kaz
    Commented Jun 9, 2016 at 20:54
  • 3
    That's not the same -- the . trick you're talking about is appending /. to the source.
    – Barmar
    Commented Jun 9, 2016 at 20:58
24

The problem with cp/mv/ln as they were originally designed is that they're two commands in one (copy to and copy into).

cp A B

is either copy A to B or copy A into B (copy A to B/A) depending on whether B exists and is a directory or not (and more variations if B is a symlink to a directory).

That's bad because it's ambiguous. So the GNU implementations have added options to work around that.

cp -T A B

copies A to B regardless. If B exists and is a directory, that will fail (unless you pass -r). In any case, you will not end-up with a A file inside B when you intended A to be copied to B.

And:

cp -t B A

is the copy into.

2
  • The original unix philosophy is to assume you know what you do, and will happily allow you to shoot yourself in the foot.
    – Lenne
    Commented Jun 10, 2016 at 5:51
  • 6
    @Lenne, but here it doesn't give you a way to avoid shooting yourself in the foot. If someone creates a B directory or symlink to some directory just before you run cp A B the command will not do what you intended. And doing [ -e B ] || [ -L B ] || cp A B still has a race condition which cp -Tn A B doesn't have. Commented Jun 10, 2016 at 5:57
6

The -T can provide a failure if a directory incorrectly exists for what should be a destination file:

$ mkdir mustbeafile
$ touch afile
$ cp -T afile mustbeafile
cp: cannot overwrite directory `mustbeafile' with non-directory
$ echo $?
1
$ cp afile mustbeafile
$ 

That is, instead of success-on-unexpected-copy-to-a-subdir, a warning and not-good exit status happens, which could then cause a script to abort, and human inspect why there's a directory where there shouldn't be one.

0

Using a flag is also much clearer, and has less risk of unintended effects, when the command is used in a script instead of manually entered. Patching dots onto paths in a script could end up in all kinds of unexpected mischief.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .