I have a shell script which checks for windows line endings.
set -e
(! git ls-files | xargs grep -I $'\r')
I am using the !
character to negate the return code of the command. Grep will return code 0
when a file with carriage return is found, and !
negates the value to the return code is then 1
and the script exits. When used with grep
(no xargs
) this works without parentheses. When xargs
is used the negation takes place according to $?
, as echo $?
will print 1
, however the script does not exit! After adding parentheses around the whole command, it works as expected. Why are the parentheses needed?
Your problem has nothing to do with xargs
.
The -e
option of bash is a little tricky.
-e Exit immediately if a pipeline (which may consist of a single simple command), a list, or a compound command , exits with a non-zero status. The shell does not exit if the command that fails is part of the command list immediately following a
while
oruntil
keyword, part of the test following theif
orelif
reserved words, part of any command executed in a&&
or||
list except the command following the final&&
or||
, any command in a pipeline but the last, or if the command's return value is being inverted with!
.
Let's look at a much simpler example:
$ cat exit_on_error_test.sh
#!/bin/bash
trap 'echo Interrupted because of an error' ERR
set -e
! true
echo Exit status: $?
$ ./exit_on_error_test.sh
Exit status: 1
$
So, even though the exit status of "! true
" was non-zero, the script was allowed to run to the end and output the value of the exit status. That's because we didn't have any failing command - the non-zero exit code was due to deliberate negation.
However, if we enclose "! true
" in parentheses we introduce a failing (compound) command.
$ cat exit_on_error_test.sh
#!/bin/bash
trap 'echo Interrupted because of an error' ERR
set -e
(! true) # This as a whole is now a failing (compound) command
echo Exit status: $?
$ ./exit_on_error_test.sh
Interrupted because of an error
$