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
  • 369,824
Gradient
  • 3,659
  • dotglob is translated to globdots by [tag:zsh] which is invalid option name for [tag:bash]. –  Aug 27 '17 at 17:21

5 Answers5

48

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).

22

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
  • 369,824
  • 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 – 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 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

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,941
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,553
  • 1
  • 27
  • 31
2

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.