Take the 2-minute tour ×
Super User is a question and answer site for computer enthusiasts and power users. It's 100% free, no registration required.

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?

share|improve this question
add comment

migrated from stackoverflow.com Apr 1 '11 at 9:14

This question came from our site for professional and enthusiast programmers.

8 Answers

up vote 19 down vote accepted

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
share|improve this answer
    
That's only a problem if you use -r. Without -r, it will just skip the directories. –  Mikel Apr 21 at 1:44
    
And the example I give using -R should work just fine too. If you think it's wrong, please explain why. –  Mikel Apr 21 at 1:48
add comment

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
share|improve this answer
    
Using dotglob OP'll also copy all non-hidden files, which is not what he wants. –  peoro Apr 1 '11 at 0:07
    
@peoro, you should read the question again. All files in A is hidden. He just want to copy that. –  kurumi Apr 1 '11 at 0:14
add comment

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

cp A/.* B/
share|improve this answer
    
This will also pass A/. and A/.. as parameters to cp, which could be problematic in case OP also needs to copy directories (using cp -r) –  peoro Apr 1 '11 at 0:09
add comment

If they all start with a dot, just use A/.*:

[holt@Michaela test]$ cp A/* B
cp: cannot stat `A/*': No such file or directory

[holt@Michaela test]$ cp A/.* B
cp: omitting directory `A/.'
cp: omitting directory `A/..'
[holt@Michaela test]$ ls -al B
total 8
drwxrwxr-x. 2 holt holt 4096 2011-03-31 16:57 .
drwxrwxr-x. 4 holt holt 4096 2011-03-31 16:57 ..
-rw-rw-r--. 1 holt holt    0 2011-03-31 16:57 .a
-rw-rw-r--. 1 holt holt    0 2011-03-31 16:57 .b
-rw-rw-r--. 1 holt holt    0 2011-03-31 16:57 .c

Hope this helps!

share|improve this answer
add comment

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.

share|improve this answer
    
why should you use a for loop and then create extra grep process just to do a job like cp A/.* ? cp will ignore the "." and ".." directories. –  kurumi Apr 1 '11 at 0:40
add comment

You can use this script

IFS=$'\n';for act in $(find A -d 1); do cp -R "$act" B; done

share|improve this answer
add comment

copy:

cp A/.* B

list:

ls -l A/.*

list files without descending directories:

ls -ld A/.*
share|improve this answer
add comment

You must exclude . and .. from the files list passed to cp!

This is safe:

ls -1d A/.[a-zA-Z]* | xargs -i cp -rp {} B
share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.