14

I am essentially trying to implement a function which asserts the failure (non-zero exit code) of another command, and prints a message when it fails.

Here is my function:

function assert_fail () {
    COMMAND=$@
    if [ `$COMMAND; echo $?` -ne 0 ]; then 
        echo "$COMMAND failed as expected."
    else
        echo "$COMMAND didn't fail"
    fi
}

# This works as expected
assert_fail rm nonexistent

# This works too
assert_fail rm nonexistent nonexistent2

# This one doesn't work
assert_fail rm -f nonexixtent

As soon as I add options to the command, it doesn't work. Here is the output of the above:

rm: cannot remove `nonexistent': No such file or directory
rm nonexistent failed as expected.
rm: cannot remove `nonexistent': No such file or directory
rm: cannot remove `nonexistent2': No such file or directory
rm nonexistent nonexistent2 failed as expected.
rm -f nonexistent didn't fail

I have tried putting double quotes around the commands, to no avail. I would expect the third invocation in the above to produce similar output to the other two.

I appreciate any/all help!

1
  • 1
    You should use COMMAND="$@" (quote the arguments) and if $COMMAND; then (test the exit status directly, instead of echoing and capturing the exit status for explicit comparison). Commented Oct 15, 2012 at 19:13

2 Answers 2

13

@rici correctly pointed out the issue you're seeing, but there are a couple of real problems with your wrapper function. First, it doesn't correctly preserve spaces (and some other funny characters) in arguments. COMMAND=$@ (or COMMAND="$@") merges all of the arguments into a single string, losing the distinction between spaces between arguments and spaces within arguments. To keep them straight, either use "$@" directly without storing it in a variable, or store it as an array (COMMAND=("$@"), then execute it as "${COMMAND[@]}"). Second, if the command prints anything to stdout, it'll wreak havoc with your exit status check; just test it directly, as @chepner said. Here's my suggested rewrite:

function assert_fail () {
    if "$@"; then 
        echo "$* didn't fail"
    else
        echo "$* failed as expected."
    fi
}

Note that the way I did the echo commands does lose the distinction of spaces within arguments. If that's a problem, replace the echo commands with this:

printf "%q " "$@"
echo "didn't fail"

and

printf "%q " "$@"
echo "failed as expected."
Sign up to request clarification or add additional context in comments.

1 Comment

You're right, that would have caused me problems later on. Thanks. +1
5

rm -f never fails on non-existent files. It has nothing to do with your wrapper. See man rm:

OPTIONS
       -f, --force
              ignore nonexistent files, never prompt

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.