cp with a single argument containing wildcards

65,218

Solution 1

This is not a bug in the cp command. When you enter cp *.pdf, cp never sees the actual wildcards because the wildcards are expanded by bash, not by cp. How will cp know that you have entered only one argument? This is a side effect of bash wildcards and cannot be called a bug.

Solution 2

You seem to understand what is happening perfectly well. In your example, *pdf indeed expands to file1.pdf file2.pdf this_is_a_folder.pdf. I don't see what's confusing you. cp is doing exactly what it should, you are telling it to copy file1.pdf and file2.pdf into this_is_a_folder.pdf and that is exactly what it is doing. There is no bug, it is working as advertised.

Since your folder's name ends in .pdf, it is included in *.pdf and since it is a folder and the last argument (sorted alphabetically), cp copies the files into it. To get the behavior you expect, you need to protect the wildcard from the shell so it is not expanded before cp sees it:

$ cp "*pdf"
cp: missing destination file operand after `*pdf'
Try `cp --help' for more information.

Note that in this case, the wildcard is not expanded so cp is actually looking for a file called *.pdf. So even if you were to call it with a directory as the last argument (cp "*.pdf" foo/) it would complain about cannot stat '*.pdf': No such file or directory. You will also see your expected behavior if you try cp *pdf in a directory that only contains one file that ends in .pdf because *.pdf will be expanded to only one argument:

$ ls -l
total 0
-rw-r--r-- 1 terdon terdon 0 Aug  5 16:56 file1.pdf
$ cp *pdf
cp: missing destination file operand after `file1.pdf'
Try `cp --help' for more information.

Also compare with this:

$ ls -l
total 0
drwxr-xr-x 1 terdon terdon 0 Aug  5 16:56 a_folder.pdf
-rw-r--r-- 1 terdon terdon 0 Aug  5 16:56 file1.pdf
-rw-r--r-- 1 terdon terdon 0 Aug  5 16:56 file2.pdf
$ cp *pdf
cp: target `file2.pdf' is not a directory

Here, since the folder's name begins with an a, *.pdf is expanded to: a_folder.pdf file1.pdf file2.pdf. Therefore, the cp command actually being run is

cp a_folder.pdf  file1.pdf  file2.pdf

Which returns an error because the last argument is not a directory.

Solution 3

As the other answers already pointed out, bash expands the wildcard and then passes what it sees to cp. In your case, cp sees file1.pdf file2.pdf this_is_a_folder.pdf. Now let's prevent it.

  • Don't use wildcards.
  • Use the -t, --target-directory switch and specify the target.
  • Always declare at the very end a destination after using a wildcard.

    cp *.pdf /I/want/to/copy/files/here
    

Solution 4

It's not a bug, it's a side-effect doing of wildcard expansion once in the shell rather than implementing it in every program.

Now, there are some much stranger ways you can trip yourself up with this, especially by creating files whose names start with "-". For example (in an empty directory):

$ touch -- --version
$ ls
--version
$ rm *
rm (coreutils) 5.2.1
Written by Paul Rubin, David MacKenzie, Richard Stallman, and Jim Meyering.

Copyright (C) 2004 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
$ ls
--version

The wildcard '*' expands to the filename which gets interpreted as a switch.

You can also cause chaos by putting spaces, tabs and newlines in file names.

Solution 5

If the last part parameter is a directory, cp will copy the specified files to it. It does not care if you named your directory foo.pdf (why such a name?!). Wildcard expansion is based on the file name (or directory name), not the file type.

With two arguments, the target can be either a file or directory. In the case of a file, the target file is removed and replaced by a copy of the source file. If the target is a directory, a copy of the source file is placed in that directory. Note one of the given synopsis:

cp [OPTION]... [-T] SOURCE DEST

With three or more arguments, the target is treated as a directory:

cp [OPTION]... SOURCE... DIRECTORY
Share:
65,218

Related videos on Youtube

Tulains Córdova
Author by

Tulains Córdova

I seek not to know all the answers, but to understand the questions. - Kwai Chang Caine I've been programming for more than two decades. I specialize in database design, SQL, PL/SQL, Unix Shell Scripting, Java SE, OOP, OOD, good practices, SOLID principles, software patterns and code quality. Manipulating and processing data with scores of different tools is something I do often. I've done extensive work automating routinary tasks by leveraging Shell Scripting. I like to improve software usability and user experience. Fledgling DBA with 6 years of experience but with an awful lot to learn. Spanish is my mother tongue and I'm fluent in english. I can read technical french.

Updated on September 18, 2022

Comments

  • Tulains Córdova
    Tulains Córdova almost 2 years

    If I have the following 2 files and 1 folder:

    someuser@computer:~/Desktop/test$ ls -l
    total 340
    -rw-r--r-- 1 someuser someuser  45082 ago  5 09:56 file1.pdf
    -rw-r--r-- 1 someuser someuser 291836 ago  5 09:56 file2.pdf
    drwxrwxr-x 2 someuser someuser   4096 ago  5 09:56 this_is_a_folder.pdf
    

    And I run the following command (notice that I ommit the destination):

    cp *.pdf
    

    file1.pdf and file2.pdf are copied into the this_is_a_folder.pdf folder.

    someuser@computer00:~/Desktop/test$ ls this_is_a_folder.pdf/
    file1.pdf  file2.pdf
    

    Obviously *.pdf is expanding into matching items, so it's equivalent to

    cp file1.pdf file2.pdf this_is_a_folder.pdf
    

    ... and as this_is_a_folder.pdf is a folder, then the two files are copied to it.

    Is this a bug ?

    It's obviously a side effect of wildcard expansion and it's not what I would expect to happen.

    I would have expected a missing destination file error.

    • manatwork
      manatwork almost 11 years
      Run echo cp *.pdf to see what gets executed. You omitted nothing.
    • LarsH
      LarsH almost 11 years
      You understand what the shell's wildcard expansion is doing. Why then would you have expected a missing destination file error?
    • Tulains Córdova
      Tulains Córdova almost 11 years
      @LarsH Principle of less surprise ? I understand that after it happend to me, not defore.
    • user
      user almost 11 years
      Principle of least surprise? What should ls *.pdf expand to in such a scenario, or for f in *.pdf; do ... done? I'd prefer wildcards expand to the same things regardless of command.
    • Tulains Córdova
      Tulains Córdova almost 11 years
      @MichaelKjörling The expanding is OK, but obviously a person that executes cp *.pdf is not expecting it to copy some files to an uknown folder.
    • user
      user almost 11 years
      So how would cp know that you used shell wildcard globbing? How would it know that you want some behavior different from e.g. ls? What if you really wanted to copy all files and subdirectories with names ending in .pdf into a subdirectory named all.my.pdf? Comments aren't meant for discussion, but I'd say it is far from "obvious" that what you are proposing is better or more reasonable to expect. (I for one rather memorize one rule, that any wildcard glob will expand to everything that glob matches and I better deal with it, than one rule per command!)
    • Tulains Córdova
      Tulains Córdova almost 11 years
      @MichaelKjörling I used to see it like this: in cp *.pdf dest_folder, *.pdf is the SOURCE and dest_folder is the DESTINY. If I accidentally ran cp *.pdf, I ommitted the DESTINY, and only provided the SOURCE, which should raise an error.
    • user
      user almost 11 years
      Which it would have, if *.pdf had not expanded to include a valid destination location at the end of the list, as exemplified in the answers already.
    • LarsH
      LarsH almost 11 years
      "Principle of least surprise" is a valid point, but it begs the question, why should that behavior be surprising? And yes I can see why it surprised you at first, especially if you didn't know that wildcards are expanded separately by the shell; but, given that fact, it's certainly not a bug in cp, and it's hard to see how cp could be designed differently to decrease surprise. Changes that would decrease surprise would be (a) don't name a directory something.pdf; (b) don't use a wildcard pattern, such as *.pdf, that could match an existing directory; or (c) use -t as @Braiam said.
    • Tulains Córdova
      Tulains Córdova almost 11 years
      @LarsH Something is certain: If it happens again, I will no longer be surprised.
    • Carlos Campderrós
      Carlos Campderrós almost 11 years
      a shell (bash, sh, zsh, ...) is a command line interpreter, so it interprets your commands and then executes them. So the command you typed is not always the command that will be executed. And the real command can't know what did you really type (in this instance, cp only sees file1.pdf, file2.pdf and this_is_a_folder.pdf, not your wildcard.
  • Lekensteyn
    Lekensteyn almost 11 years
    @user1598390 Glob expansion is done by the shell, therefore this_is_a_folder.pdf is implied by *.pdf. cp does not know that you only want to copy a set of files.
  • Brian Rasmussen
    Brian Rasmussen almost 11 years
    But by using quoting in cp "*pdf", even if you included a destination argument, the asterisk never gets expanded and you will get a "no such file" message unless you have one named literally "*pdf".
  • LarsH
    LarsH almost 11 years
    @user1598390: Why is that a problem? (Or: why is that unexpected?)
  • terdon
    terdon almost 11 years
    @DennisWilliamson yes indeed, but it gives the error the OP expected, hence it displays the expected behavior.
  • Kevin
    Kevin almost 11 years
    This could also be a great demonstration of why you should use ls and not ls *.
  • mike3996
    mike3996 almost 11 years
    s/bash/shell/g