Re: How to disable cp error message in bash?



2008-07-17, 10:50(-07), PengYu.UT@xxxxxxxxx:
Hi,

I tried the following command. I don't what bash to report the error
message when there is no *.sh files. Is there a way to do it?

~$ cp /bin/*.sh ./
cp: cannot stat `/bin/*.sh': No such file or directory
[...]

Note that it is not bash that reports that error message, but
cp, so as others already pointed out, you may want to redirect
cp's stderr do /dev/null but that means you'll miss other errors
by cp.

However, it is indeed the shell that is failing to find any file
ending in ".sh" in the /bin directory.

There is what can be considered as a misfeature in most shells
including bash in that when a pattern doesn't match any file,
the shell doesn't complain but instead passes the pattern
untouched to the command.

So here, as /bin/*.sh doesn't match, the shell is asking "cp" to
copy a file called "/bin/*.sh". And as that file doesn't exist,
cp reports a problem.

It's not too big a problem for the "cp" command and for this
pattern. But imagine for instance:

rm -- *.[hc]

If there is no .h nor .c file, but there is a file "named"
"*.[hc]", you'll end up removing a file you didn't want to
remove.

A few shells did fix that: csh and tcsh (to some extend) and
zsh.

In bash, you can fix it by doing a "shopt -s failglob"

In those shells, if you do:

cp /bin/*.sh .

If there is no file matching that pattern, then the *shell* will
report an error and not call "cp" which is a much more sensible
thing to do. You'll still get an error message though.

With bash (+failglob) or zsh, you can do:

{ files=(/bin/*.sh); } 2> /dev/null &&
cp -- "${files[@]}" .

This way, you don't get an error message if there's no .sh file,
but you still get to see the other "cp" errors.

With zsh, you could also do:

files=(/bin/*.sh(N))
(($#files)) && cp -- $files .

(N) makes so that if there is no matching file, the pattern
expands to nothing instead of reporting an error. Then
(($#files)) which returns false if the number of elements in the
$files array is 0 is a way to only run cp if there were .sh
files.

POSIXly, you could do:

set -- /bin/[*].sh /bin/*.sh
if [ "$1$2" != '/bin/[*].sh/bin/*.sh' ]; then
shift
cp -- "$@" .
fi

--
Stéphane
.



Relevant Pages