99

I'm using Mac OS X. I'm trying to copying some files with cp command for a build script like this.

cp ./src/*/*.h ./aaa

But this command fires an error if there is no .h file in ./src directory. How to make the command don't fire the error? (silent failure) The error makes build result fail, but I just want to copy when only there are some header file.

Valentin Lorentz
  • 187
  • 2
  • 14
Eonil
  • 9,689
  • 15
  • 34
  • 53

6 Answers6

123

If you're talking about the error message, you can suppress that by sending it to the bit bucket:

cp ./src/*/*.h ./aaa 2>/dev/null

If you want to suppress the exit code and the error message:

cp ./src/*/*.h ./aaa 2>/dev/null || :
Dennis Williamson
  • 60,515
  • 14
  • 113
  • 148
  • 18
    It would be nice to explain what `:` means in this context. – Piotr Dobrogost Feb 10 '14 at 15:35
  • 34
    @PiotrDobrogost: In Bash and some other shells the colon is a null utility (no-op). It's [specified](http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#colon) by [POSIX](http://pubs.opengroup.org/onlinepubs/9699919799/). Since it always returns true, it's used here to suppress the exit code of a failed `cp` (should that be desired). The shell builtin `true` could be used instead and would be more readable. – Dennis Williamson Feb 10 '14 at 15:46
  • More about `:` – [What Is the Purpose of the `:' (colon) GNU Bash Builtin?](http://stackoverflow.com/q/3224878/95735) – Piotr Dobrogost Feb 10 '14 at 15:49
  • 3
    This will also ignore other errors (source/destination directory doesn't exist, source file exists but is not readable, disk full, read-only filesystem, IO error, `cp` not being in `PATH` somehow...) – Vladimir Panteleev Oct 24 '18 at 19:32
  • syntax error near unexpected token `||' – thang Jul 24 '19 at 20:53
  • @thang: What shell are you using? – Dennis Williamson Jul 24 '19 at 21:48
  • ubuntu's default bash. i guess linux bash is different from mac os bash. – thang Jul 25 '19 at 22:03
  • @thang: All versions of Bash at least as far back as version 2 (_ancient_) support `||` regardless of operating system. What does `false || echo "OR"` output for you? – Dennis Williamson Jul 25 '19 at 22:10
  • || seems to work in shell but doesn't when i put into shell script. i think that's the culprit – thang Jul 26 '19 at 01:39
  • @thang: How are you running your script? `bash ./scriptname` for example? Or with a shebang like `#!/bin/bash`? Or do you have stray characters in your script such as Windows/DOS line endings `\r\n`? – Dennis Williamson Jul 26 '19 at 02:55
  • #!/bin/bash everything is linux, so nothing windows. – thang Jul 26 '19 at 03:34
  • @thang: Well without seeing the actual line there's nothing else I can suggest. – Dennis Williamson Jul 26 '19 at 03:43
  • thanks for your help. i did a work around with if [-f ...] before doing cp. probably not the most elegant but it works and it's a shell script so elegance isn't so important. – thang Jul 26 '19 at 14:21
20

You're looking for something along the lines of

if [ -e file ]
 then cp file /somewhere
fi

(Unfortunately, the -f option is not the droid you're looking for.)

If you want to match a glob, that won't work; use find instead, e.g.:

find ./src -name \*.h -exec cp {} ./destination \;
Brad Ackerman
  • 2,141
  • 2
  • 17
  • 19
  • Note that there is a potential TOCTOU issue with such an approach (e.g. if you use `set -e`, and the file disappears between the `[` and the `cp` invocations, your script will crash). – Vladimir Panteleev Oct 24 '18 at 19:34
13

Old question, but might still be relevant for others.
If you don't need to use cp, you could try with rsync.
To copy all files from a source to a destination directory, run:

rsync -avzh --ignore-errors /path/to/source /path/to/destination

Rsync comes with most Unix-like systems such as Linux, Mac OS X or FreeBSD.

mre
  • 311
  • 2
  • 7
  • 5
    You could use rsync instead of cp, adding the parameter `--ignore-missing-args`: `rsync -av --ignore-missing-args ./src/*/*.h ./aaa` This has the advantage over `--ignore-errors` that the only errors ignored are those related to source files not existing. With `--ignore-errors` every error is ignored, which may be dangerous. Also, take into account that this parameter is fairly recent, so it might not be present in old versions of rsync. – jesjimher Oct 15 '15 at 10:18
  • what if the source directory doesn't exist? – Arrrow Jun 18 '20 at 07:37
  • 1
    I don't think that should be the responsibility of rsync to be honest. Rather, you can check upfront if the source directory exists, e.g. `[ -d /path/to/source ] && rsync -avzh --ignore-errors /path/to/source /path/to/destination` – mre Jun 18 '20 at 11:30
12

Piping the result to true ensures that the command will always succeed. I have tried this on Linux but not on any Mac OS:

cp ./src/*/*.h ./aaa | true
Andrew Schulman
  • 8,561
  • 21
  • 31
  • 47
Vivek
  • 221
  • 2
  • 2
  • 10
    The simple pipe `|` is always run while `||` is only done in case of an error. And `true` is usually a binary while the colon `:` is a builtin and doesn't consume a PID. – ott-- Feb 06 '15 at 18:16
  • 1
    A bash script on MacOS - Yosemite containing "cp ./src/*/*.h ./aaa" command does not error out if the .h files do not exist. – Vivek Feb 21 '15 at 07:29
  • Simple, readable, and working. This is exactly what I needed, thanks mate. – Kostiantyn Ko Jul 08 '22 at 23:36
2

You could force the correct error status. With a function:

$ cpalways () { cp $1 $2 2>/dev/null ; return 0 ; }

Given the following:

$ ls foo bar baz
ls: baz: No such file or directory
bar foo

Regular copy will return an error. It will return an exit status of 1.

$ cp baz bar ; echo $?
cp: baz: No such file or directory
1

If we use the cpalways() function above, any errors will be hidden:

$ cpalways baz bar ; echo $?
0
$ cpalways foo bar ; echo $?
0
$ cpalways baz bar ; echo $?
0
Stefan Lasiewski
  • 22,949
  • 38
  • 129
  • 184
-1

This works

$(cp ./src/*/*.h ./aaa | true)