Copy only regular files from one directory to another

179,151

Solution 1

cp dir1/* dir2

cp will not copy directories unless explicitly told to do so (with --recursive for example, see man cp).

Note 1: cp will most likely exit with a non-zero status, but the files will have been copied anyway. This may be an issue when chaining commands based on exit codes:&&, ||, if cp -r dir1/* dir2; then ..., etc. (Thanks to contrebis for their comment on that issue)

Note 2: cp expects the last parameter to be a single file name or directory. There really should be no wildcard * after the name of the target directory. dir2\* will be expanded by the shell just like dir1\*. Unexpected things will happen:

  • If dir2 is empty and depending on your shell and settings:
    • you may just get an error message, which is the best case scenario.
    • dir2/* will be taken literally (looking for a file/directory named *), which will probably lead to an error, too, unless * actually exists.
    • dir2/* it will just be removed from the command entirely, leaving cp dir1/*. Which, depending on the expansion of dir1/*, may even destroy data:
      • If dir1/* matches only one file or directory, you will get an error from cp.
      • If dir1/* matches exactly two files, one will be overwritten by the other (Bad).
      • If dir/* matches multiple files and the last match is a, you will get an error message.
      • If the last match of dir/* is a directory all other matches will be moved into it.
  • If dir2 is not empty, it again depends:
    • If the last match of dir2/* is a directory, dir1/* and the other matches of dir2/* will be moved into.
    • If the last match of dir2/* is a file, you probably will get an error message, unless dir1/* matches only one file.

Solution 2

It's the shell that expands wildcards, not the commands. So cp dir1/* dir2/* first expands the two wildcards, then calls cp on the result. This is not at all what you apparently expect: depending on how many files there are already in dir2, dir2/* may expand to one or more argument. The command cp doesn't know which of its arguments came from expanding the first pattern and which ones came from expanding the second pattern. It expects its last argument to be the name of the destination directory. Thus, to copy all the files from the directory dir1 into the directory dir2, the last argument must be the directory dir2:

cp dir1/* dir2

Since * matches all files, cp attempts to copy all files. This includes directories: directories are files too. It skips directories, but reports an error. It copies the content of special files such as named pipes (something had better be writing to them, or cp will block), etc.

To copy only regular files, you need to restrict the matching. In zsh, you can use the glob qualifier . for that:

cp dir1/*(.) dir2

Other shells don't have this. You can use the find command to filter on file types. Assuming that you're running non-embedded Linux or Cygwin:

find dir1 -maxdepth 1 -type f -exec cp -t dir2 {} +

On Linux, FreeBSD and OSX:

find dir1 -maxdepth 1 -type f | xargs -I {} cp {} dir2

Solution 3

You can use 'rsync' instead of 'cp'

rsync -vt dir1/* dir2/

It cleaner and in some cases faster

Share:
179,151

Related videos on Youtube

user1058398
Author by

user1058398

Updated on September 18, 2022

Comments

  • user1058398
    user1058398 almost 2 years

    I'd like to copy a content of directory 1 to directory 2.  However, I'd like to only copy files (and not directories) from my directory 1.  How can I do that?

    cp dir1/* dir2/*
    

    then I still have the directories issue.

    Also, all my files don't have any extension, so *.* won't do the trick.

    • Admin
      Admin over 10 years
      Don't know what extensions have to do with it.
    • Admin
      Admin over 10 years
      cp dir1/* dir2 or cp -t dir2 dir1/*
    • Admin
      Admin over 10 years
      @richard There's a fairly common habit coming from the DOS world of not using extensions for directories, which still survives to some extent, leading some people to assume that if there's a dot then it isn't a directory and vice versa.
    • Admin
      Admin over 3 years
      Avoid using quotes, neither simple nor double
  • contrebis
    contrebis over 8 years
    $ cp dir1/* dir2 --> cp: dir1/isadir is a directory (not copied). exits with status 1 for me.
  • Adaephon
    Adaephon over 8 years
    Yes, it does exit with status 1 for me, too. But even so, cp does copy the files matching dir1/* on every system I checked: Arch Linux, Ubuntu 14.04, OpenBSD 3.9 and 5.5, SuSE Linux 8.1, FreeBSD 6.2, Solaris 8, 9 and 10 (where cp actually exits with code 2). So this behavior is neither new (SuSE 8.1 is from 2002) nor is it limited to Linux. cp exits with a non-zero exit code because it could not do everything it was told to do. That does not mean it does nothing.
  • contrebis
    contrebis over 8 years
    Sure, I thought worth noting because if you're chaining commands together with && this can cause a problem. I was trying to something like this though I can't remember the context now.
  • Abdull
    Abdull almost 8 years
    Is there a reason to specify -maxdepth 1. Also, is there a way to recursively copy and preserve the original directory hierarchy in the destination directory?
  • Gilles 'SO- stop being evil'
    Gilles 'SO- stop being evil' almost 8 years
    @Abdull Without -maxdepth 1, the find command would recurse into subdirectories, which was not desired here. If you want to copy the whole directory tree including subdirectories, it's a completely different question, and the answer is a lot simpler: cp -a dir1 dir2.
  • Adaephon
    Adaephon about 6 years
    @felwithe Whether * includes hidden files or how to include hidden files depends on the shell. If the * does not include hidden files, you can match them with .*. Note that this will include . and .. on bash and dash but not on zsh. In the context of this answer you can get away with cp dir1/* dir1/.* dir2 because . and .. are directories and will not be copied. zsh can also be made to include hidden files with *(D).
  • questionto42standswithUkraine
    questionto42standswithUkraine over 2 years
    @felwithe I have just tried it, works for hidden files with cp dir1/.* dir2.