(Update: this happens on Mac OS X -- I am using Mountain Lion.) It sometimes happen that I use the tab key to complete the path, so my command may be:

cp -r ~/some/folder/ .

but it turns out that the line above has a different behavior from

cp -r ~/some/folder .

the second line will copy the folder into the current directory, as folder, but the first line will copy all 7 files, 6 subfolders into the current directory, contaminating the current directory. Since I don't do any source control using git or svn in that particular current directory, it actually has to be cleaned up manually, and it is quite a pain.

Is there a way to prevent this from happening? Such as by making a function, alias, or Bash setting? I know I can remember this, but 2 months down the road, I can actually accidentally do the same thing again, so it is better to guard against this behavior. (I would rather needing to use cp -r ~/some/folder/* . and cp -r ~/some/folder/.* . if that is what I really want.)

  • That behavior for the first example sounds strange and wrong and I am unable to reproduce it with GNU coreutils cp. What version / OS do you have? – jw013 Nov 19 '12 at 15:31
  • 1
    I was about to say my system does not behave this way. Neither on ubuntu on my laptop, centos server I control, or even cygwin at work.. – Drake Clarris Nov 19 '12 at 15:36
  • Maybe he was really using rsync but changed it to cp for the question? – jordanm Nov 19 '12 at 15:43
  • 2
    It actually works this way on BSD/Mac OS. -R ... If the source_file ends in a /, the contents of the directory are copied rather than the directory itself. man cp 1 – Mikel Nov 19 '12 at 15:59
  • yeah never tried a bsd flavor, so was not aware. Interesting to note, thanks! – Drake Clarris Nov 19 '12 at 18:51

To do this right would require re-parsing all of cp's options, and that's not a good idea. But here's something that should come close:

# run cp command as-given, unless any argument ends in a slash
safecp() {
  for arg; do
    case $arg in */)
      echo "safecp: trailing slash in cp $*" >&2
      return 1
  cp "$@"

Alternatively, try putting

set mark-directories off

in your ~/.inputrc to make bash not automatically add a slash when tab-completing directories.

  • or can we make cp behave this way: if the source has a trailing /, then stop with a warning, so we actually need to use \cp or somehow bypass the function if that's what we desire. If there is no trailing / in the source, then just proceed with the usual cp. I think not adding slash when tab-completing could be quite troublesome because we need to add the slash ourselves each time if there are several levels down for a path. – nonopolarity Nov 20 '12 at 20:39

You could certainly write a short bash function (with a recent version of bash, which can do regular expressions it's extremely simple), that would catch this kind of errors:

function cpr () {
    local src=...
    if [[ "$src" =~ /$ ]]; then
        # handle error
        # proceed with copying

the tricky part is determining what dest should be (you certainly want to skip the destination directory), if you want to cover all of cp's options combinations (at least the GNU coreutils' cp has 3 different variations of syntax).

Using a specialised function that would correct it in-place might be better:

function cpr () {
    cp -R ${1%%/} ${2}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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