How do I copy file named starting with a dot?

I am trying to copy all files under directory A to directory B. All files under directory A are starting with dot, for example:

A/.a
A/.b
A/.c

which I found if I use: cp A/* B, always get error:

cp: cannot stat 'A/*': no such file or directory

It seems there is no option for cp as ls to handle entries started with dot, anyone has idea how to fix it?


The reason is because in bash, * does not include files starting with dot (.).

You can run

cp A/.* B

It will warn you that it did not copy . or .., or any subdirectories, but this is fine.

Or, if you want to copy dot files and normal files together, run

cp A/.* A/* B

You could also run

shopt -s dotglob
cp A/* B

which will work in bash, but not sh.

And if you don't mind subdirectories being copied too, then this is the easiest:

cp -R A/ B

Tip: If ever wildcards aren't doing what you expect, try running it with echo, e.g.

$ echo A/*
A/file1 A/file2

$ echo A/.*
A/. A/.. A/.hidden1 A/.hidden2

$ echo A/.* A/*
A/. A/.. A/.hidden1 A/.hidden2 A/file1 A/file2

$ shopt -s dotglob
$ echo A/*
A/file1 A/file2 A/.hidden1 A/.hidden2

If bash, you can set dotglob before you copy

shopt -s dotglob
cp A/* /destination

Or a programming language

$ ruby -rfileutils -e  'Dir[".*"].each {|x| FileUtils.copy(x,"/destination") if File.file?x}'

If you don't want to set dotglob, just

cp A/.* /destination 2>/dev/null

What you're looking for is more along the lines of:

cp A/.??* B/

This will match all dotfiles, but not "." or "..". Most of the above solutions are fine as long as you're not working recursively. But as soon as you want to do something like:

cp -R A/.??* B/

Without omitting ".." you'll copy everything from the parent directory on down, including non-dotfiles.


I justed tried the following and it works just find...

cp A/.* B/

That's not cp's fault, it's bash: bash expands * in all the non-hidden (ie: non starting with .) files.

Bash will expand .* (thus A/.*, in your case) with all the files starting with ., but unluckily it also includes . and .. (current and parent directories) which you will probably want to skip. (Note that other shells, like zsh, wouldn't include them, and IIRC also bash, after setting some options).

An easy solution could be to remove . and .. from files matched by .*, in a (very) hacky way like this one:

cp $( for F in A/.*; do echo $F | grep -v "^\.*$"; done ) B

or this one (probably cleaner: it uses find to find the files to copy):

cp $( find A -maxdepth 1 -mindepth 1 -name ".*" ) B

but you'll likely find cleaner solutions.