I noticed that whenever I use a command cp -R foo/* I copy everything except files like .env or probably .gitignore

The solution is to use cp -R foo/ as it copes the whole directory, but this does not change the fact that I still don't understand the behaviour of using a * character.

Why a * character won't catch a files like .env?

The OS is Debian

  • cp -R foo/ copies recursively everything under foo/
  • cp -R foo/* copies recursively all the files and directories that match foo/*. And by default names with a dot are not included (ls * won't list them either). This is explained here (as well as some other little details about the leading dots).

Besides the handling of files with leading dots, another difference is that cp -R foo/* copies the individual files and directories in foo/ to the target directory, while cp -R foo/ makes a copy of foo/ in the target directory (so you get one more directory level).


By default globbing in bash does not include filenames starting with a . So when you specify * you are asking to copy recursively without files starting with "." . When you do not specify * it copies everything including "." files.

  • What you are saying is (I suppose) based from what I wrote, but I never saw this specified in any documentation, for example tldp.org says that asterix character "can represent any number of characters (including zero, in other words, zero or more characters). If you specified a "cd*" it would use "cda", "cdrom", "cdrecord" and anything that starts with “cd” also including “cd” itself. "m*l" could by mill, mull, ml, and anything that starts with an m and ends with an l.". – Bartłomiej Sobieszek Aug 17 '17 at 9:30
  • check below for more details for same – Vishal Shinde Aug 17 '17 at 9:41
  • man 7 glob ..... – Vishal Shinde Aug 17 '17 at 10:16
  • Just as a suggestion, you could use rsync to copy everything, including dot files and dot directories. – Paulo Aug 17 '17 at 22:13
  • I'd like to point out that were you to do this as a superuser, the dot-files would then be picked up by the *, as files are not generally hidden from root. – Nevin Williams Aug 19 '17 at 1:22

In addition to the other answers you have, there are a couple of other cases where foo/ and foo/* could be different (not just files beginning with .). Besides, if you wanted to have files that start with . matched by your glob, you can enable that too with shopt -s dotglob.

The first is if there are no entries in foo/ and you have not enabled nullglob then foo/* will be returned as a literal passed to cp. Since (in this case) there isn't a source foo/* cp would complain, while cp -R foo/ would always have at least foo/ to copy. If you did enable nullglob then foo/* would expand to nothing, so you'd end up missing an argument to cp.

Another case to consider is if there are a LOT of entries in foo/. The shell expands the glob and then invokes the process, but if the glob expands to too many arguments you'll get an error. With cp -R foo/ you only have 2 arguments (though presumably you'll have a target somewhere else).

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

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