22

Situation :

$ mkdir foo && touch foo/.test
$ cp foo/* .
zsh: no matches found: foo/*
(or bash : cp: cannot stat ‘foo/*’: No such file or directory)

I have a directory full of hidden folders and files. What is happening and what is the solution?

slm
  • 363,520
  • 117
  • 767
  • 871
Gradient
  • 3,579
  • 10
  • 31
  • 37
  • `dotglob` is translated to `globdots` by [tag:zsh] which is invalid option name for [tag:bash]. –  Aug 27 '17 at 17:21

5 Answers5

45

With zsh, the typical way is to use the D glob qualifier (to include [D]ot files):

cp foo/*(D) .

Note that with or without D, zsh globs never include . nor .. (as you'd expect).

Stéphane Chazelas
  • 522,931
  • 91
  • 1,010
  • 1,501
21

Disclaimer: This answer deals with Bash specifically but much of it applies to the question regarding glob patterns!

The star character (*) is a wildcard. There are a certain set of characters that it will take the place of and the first character being a dot (.) isn't one of them. This is a special case just because of how the Unix filesystems work, files that start with a dot are considered "hidden". That means that tools such as cp, ls, etc. will not "see" them unless explicitly told to do so.

Examples

First let's create some sample data.

$ mkdir .dotdir{1,2} regdir{1,2}
$ touch .dotfile{1,2} regfile{1..3}

So now we have the following:

$ tree -a
.
|-- .dotdir1
|-- .dotdir2
|-- .dotfile1
|-- .dotfile2
|-- regdir1
|-- regdir2
|-- regfile1
|-- regfile2
`-- regfile3

Now let's play some games. You can use the command echo to list out what a particular wildcard (*) would be for a given command like so:

$ echo *
regdir1 regdir2 regfile1 regfile2 regfile3


$ echo reg*
regdir1 regdir2 regfile1 regfile2 regfile3

$ echo .*
. .. .dotdir1 .dotdir2 .dotfile1 .dotfile2

$ echo .* *
. .. .dotdir1 .dotdir2 .dotfile1 .dotfile2 regdir1 regdir2 regfile1 regfile2 regfile3

$ echo .dotdir*
.dotdir1 .dotdir2

Changing the behavior?

You can use the command shopt -s dotglob to change the behavior of the * so that in addition to files like regfile1 it will also match .dotfile1.

excerpt from the bash man page

dotglob If set, bash includes filenames beginning with a `.' in the results 
        of pathname expansion.

Example:

$ shopt -s dotglob
$ echo *
.dotdir1 .dotdir2 .dotfile1 .dotfile2 regdir1 regdir2 regfile1 regfile2 regfile3

You can revert this behavior with this command:

$ shopt -u dotglob
$ echo *
regdir1 regdir2 regfile1 regfile2 regfile3

Your situation?

For you you're telling cp that you want to copy all the files that match the pattern *, and there aren't any files.

$ cp foo/.* .

Or you can do this if you want everything in the foo folder:

$ cp foo .

Or you can be explicit:

$ cp foot/.* foo/* .

A more compact form using brace expansion in bash:

$ cp foo/{.,}* .

At any time you can use the echo trick to see what your proposed file patterns (that's the fancy term for what the star is a part of).

$ echo {.,}*
. .. .dotdir1 .dotdir2 .dotfile1 .dotfile2 abc regdir1 regdir2 regfile1 regfile2 regfile3

Incidentally if you're going to copy a directory of files + other directories, you typically want to do this recursively, that's the -R switch to cp:

$ cp -R foo/. .
slm
  • 363,520
  • 117
  • 767
  • 871
  • 1
    You might want to mention the use of `shopt -s dotglob`, `echo *` then gives: `.dotdir1 .dotdir2 .dotfile1 .dotfile2 regdir1 regdir2 regfile1 regfile2 regfile3` – Drav Sloan Sep 07 '13 at 09:32
  • @DravSloan - thanks for the suggestion. Added it. – slm Sep 07 '13 at 09:44
  • `cp -r foo .` will typically not work since `cp` will refuse to copy `foo` over itself, you probably meant `cp -R foo/. .`. (note that `cp -R` is the standard one). – Stéphane Chazelas Sep 07 '13 at 15:00
  • 1
    Also note that (quite sensibly), `zsh` doesn't include `.` and `..` in the expansion of `.*`. In `zsh`, if a pattern doesn't match, the command is aborted (quite sensibly again), so in the general case, `cp foo/{,.}* .` will fail if there's no hidden or no non-hidden files. – Stéphane Chazelas Sep 07 '13 at 15:04
  • @StephaneChazelas - thanks for keeping me honest. Fixed suggestions. Also from the `cp` man page it shows this: `-R, -r, --recursive`. I would take that to mean that any form of the switch is acceptable. – slm Sep 07 '13 at 15:07
  • @StephaneChazelas - yeah I never could get used to `zsh`, still prefer `bash` even with all its warts. – slm Sep 07 '13 at 15:09
  • You're reading the man page of GNU `cp`. GNU stands for `GNU's Not Unix`. Here, we're on `Unix and Linux`, `Unix` being in the largest sense of the word a family of operating systems and Linux the kernel found in a number of Unix-like operating systems. As such, GNU `cp` is just one of many implementations of the standard Unix `cp` command. The specification of the standard `cp` can be found [here](http://pubs.opengroup.org/onlinepubs/9699919799/utilities/cp.html) – Stéphane Chazelas Sep 07 '13 at 15:44
  • @StephaneChazelas - I figured that was the case. I understand we're on U&L, and appreciate your attention to the subtle differences (it's always an education on the finer points in both reading and chatting with you), but does it not seem to be a safe assumption? Or is your comment meant more to educate me and also passerby's, or a little of both? 8-) – slm Sep 07 '13 at 15:53
  • 10
    This answer applies to bash only. The asker is using zsh, where the answer is [a lot simpler](http://unix.stackexchange.com/questions/89749/cp-hidden-files-with-glob-patterns/89771#89771) and `shopt` doesn't exist. – Gilles 'SO- stop being evil' Sep 07 '13 at 21:56
  • @Gilles - I added a note at the top mentioned that this was Bash specific. Also the link in referenced didn't appear to work, it took me back to this question. – slm Sep 07 '13 at 22:01
  • The link is to Stephane's answer. – Gilles 'SO- stop being evil' Sep 07 '13 at 22:05
  • @Gilles - weird when I clicked on it it jumps to the top of the Q. I figured that was what you were linking to. – slm Sep 07 '13 at 22:08
  • The special meaning of a leading `.` meaning hidden files has nothing to do with _Unix file systems_. That's just a convention in applications. – Stéphane Chazelas Sep 17 '15 at 10:42
4

The shell is treating those files as hidden when it resolves the * character, so cp doesn't receive any of these file names as arguments.

You can copy them by explicitly specifying cp foo/.* .

replay
  • 8,483
  • 1
  • 26
  • 31
3

Stéphane Chazelas's zsh answer is correct for a single expression with a glob in it. However, if you want to set it for all expressions by default, you can use

setopt GLOB_DOTS

Put it in your ~/.zshrc to make it permanent.

Sparhawk
  • 19,561
  • 18
  • 86
  • 152
1

If what you're wanting to do is copy all files & directories from one place to another, you could use the standard rsync command. In the example you give above:

mkdir foo && touch foo/.test
rsync -a foo/ .

will recursively copy all of the contents of foo, including hidden files and hidden directories, into the current directory. The trailing slash at the end of foo/ is important to rsync; with it only the contents of foo are copied, without it rsync will copy foo as well. For example:-

mkdir src && mkdir dest && touch src/.test
rsync -a src  dest    // copies 'src' contents to 'dest/src'
rsync -a src/ dest    // copies 'src' contents to 'dest' 

There are many other options available tweak rsync, including copying between machines.

Rob Swarbrick
  • 96
  • 1
  • 3