15

How to copy hidden files and hidden subdirectories (the ones starting with a dot) in folder A to folder B? For example if I have this structure:

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

I would like to copy to B just the hidden files and hidden subdirectories in A:

B/.a
B/.b/
B/.b/somefile
B/.b/.c

I have already tried this command: cp A/.* B from this other superuser question. However, it does not copy the subdirectories. Also tried cp -r A/.* B, but it copies . so I end with an exact copy of A (including the normal files). Any help is appreciated.

5 Answers 5

23

As long as you're only looking for hidden files and folders at the level of A and don't want, for example

A/b/.hidden

to be copied, you should be able to use this:

cp -r A/.[^.]* B

It basically means copy anything that starts with a . and then any character other than a . That filters out . and ..

Edit: Removed the -p from the cp command since Asker hasn't indicated he wants to preserve any ownerships, dates, etc.

2
  • This works for the example file and directory names given in the question, but the text of the question says “hidden files and hidden subdirectories (the ones starting with a dot)”, and this answer will not find files and directories whose names begin with two dots; e.g., ..c. Commented Oct 27, 2014 at 22:27
  • That's quite an edge case, but a legitimate concern none-the-less. I hadn't considered that. You could account for that by switching to .*[^.] but then you'd miss files that end with a .. I think you would indeed need extended globbing to truly account for all cases. Commented Oct 28, 2014 at 13:08
5

The problem with A/.* is that there is the directory . in A which also matches the pattern.

You can turn on extended glob patterns and use the following:

shopt -s extglob
cp -r A/.!(?(.)) B    

It matches files whose name starts with a dot and whose second character is neither a dot nor nothing ( ?(.) matches nothing or a dot, !(...) negates it, i.e. !(?(.)) matches everything else than nothing or a dot).

1
  • +1 for a correct answer. Note that .!(@(|.)) is (AFAICT) equivalent to the above, (IMNSHO) a little clearer, and only one character longer. Commented Oct 27, 2014 at 22:28
5

For cases like this would recommend using find instead of cp like this:

find A/ -type f -maxdepth 1 -name '.*' -exec cp -p {} B/ \;

The basic syntax breaks down like this:

  • find A/ -type f: find items in the directory A/ whose type is a file (instead of a directory)…
  • -maxdepth 1 -name '.*': To this for a maxdepth of 1 directories and whose name begins with ..
  • -exec cp -p {} B/ \;: And once these files are found, exec the cp command with a -p flag to preserve dates/times from the source ({}) to the destination of B/.

I like using maxdepth to add a layer of control so I am not accidentally copying a whole filesystem. But feel free to remove that.

0
0
 for item in `find A -type d | grep -E "\."` ; do cp -r $item B ; done
  • find A -type d provides a recursive list within A with only directories
  • grep -E "\." filters directories with a dot (i.e.: hidden directories)
  • the -E option was needed here because without it it means "current directory" as well
  • the backslash is to avoid the meaning, under regexp, of "any character"
  • cp -r to copy recursively

I have created the files and folders structure for A and executed the command in Git Bash (I'm not with a linux just right now) and it worked.

8
  • This breaks if files have whitespace or special characters in their name or path.
    – slhck
    Commented Oct 23, 2014 at 16:30
  • Thanks for noticing :) Actually I limitied to the "test case" by @gaboroncancio. If you can give me other test battery I may try to improve that (of course if you want, improve it by yourself either editing this response or creating a new answer)
    – malarres
    Commented Oct 24, 2014 at 10:39
  • You could simply put the dotfiles in a folder called A B, and then it'd act unexpectedly because it'd expand to cp -r A B/.dotfile B. The general advice is not to parse find or ls output at all. If you use find you should also use its own options for filtering rather than grep, and if you pipe find output somewhere else, use -print0, or directly call the command you want. See the find manual.
    – slhck
    Commented Oct 24, 2014 at 10:45
  • Even more generally, when working with files it's safest to use shell globs as explained in other answers (although they often require extglob to be set).
    – slhck
    Commented Oct 24, 2014 at 10:46
  • Thanks for the link. Let's leave the find parsing then.
    – malarres
    Commented Oct 27, 2014 at 7:22
0

As an alternative you can use this other command if the second character is alphanumeric (source):

cp -r A/.[a-zA-Z0-9]* B
2
  • This works for the example file and directory names given in the question, but the text of the question says “hidden files and hidden subdirectories (the ones starting with a dot)”, and this answer will not find files and directories whose names begin with a dot and a special character; e.g., .@foo or ..c. Commented Oct 27, 2014 at 22:29
  • That is why I point that it works if the second character is alphanumeric. ;) Commented Oct 27, 2014 at 22:41

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .