2

I'm working on a test script. I want a particular test to run when another test does not return a failure. Here's is the basic test I am using, and its assigned to a shell script variable:

cpp -dM -fsanitize=undefined < /dev/null 2>&1 | grep -i -c error

If its GCC 4.8 or Clang 3.2 (or above), then the Undefined Behavior sanitizer is available and the value returned from grep is 0. If the compiler lacks the support, then the value is non-0. For example:

# Clang 3.1
$ cpp -dM -fsanitize=undefined < /dev/null
clang: error: unsupported argument 'undefined' to option 'fsanitize='

If I try to use grep -v -c, then lines not matching "error" are counted, which effectively counts the lines dumped by the preprocessor. So I get a result of 248 (no error) and 1 (with error), but I never get a 0-value on failure.

Later, for readability, I perform:

if [ "$HAVE_UBSAN" -ne "0" ]; then
  export CXXFLAGS="-DNDEBUG -g2 -O2 -fsanitize=undefined $MY_CXXFLAGS"
  make ...
  # Run self tests, scrape output for failures
fi

The problem is I must negate the count returned from the test grep -c error to maintain readability. That is, I must turn the result into 0 (does not have) or non-0 (does have).

In C/C++, I would perform the equivalent to (notice the ! on the expression):

!(`cpp -dM -fsanitize=undefined < /dev/null 2>&1 | grep -i -c error`)

How do I invert the count returned from grep?


I don't have this problem with the Valgrind test because I can use the count directly:

 HAVE_VALGRIND=`which valgrind | grep -i -c valgrind`
4

In these cases you can check the return value of cpp directly:

HAVE_UBSAN=$(cpp -dM -fsanitize=undefined < /dev/null > /dev/null 2>&1 && echo 1)
if [ "$HAVE_UBSAN" = 1 ]
. . .

or even

if cpp -dM -fsanitize=undefined < /dev/null > /dev/null 2>&1
then
    export CXXFLAGS="-DNDEBUG -g2 -O2 -fsanitize=undefined $MY_CXXFLAGS"
    make ...
    # Run self tests, scrape output for failures
fi

alternatively, and getting to the point of your question, to negate the sense of the comparison, that is, to check for failure of cpp, is very similar to the C++ example you gave, use !:

    if ! cpp -dM -fsanitize=undefined < /dev/null > /dev/null 2>&1
    then
        echo 'NO sanitizer'
    fi

A similar technique can be used to set VALGRIND:

    VALGRIND=$(which valgrind > /dev/null && echo 1)

Since you know C++ you'll be familiar with && and ||. They work similarly on the shell command line. In the above, if which valgrind > /dev/null is successful, then "1" will be echoed..

  • Thanks Robert. I am testing this now. (Sometimes, you just can't see the forest through the trees...) – user56041 Oct 27 '15 at 6:19
  • A quick question... Are there any one liner's to invert the result of the command? Or do I need to use an if .. then.. else...? I'm trying to keep things small and readable. – user56041 Oct 27 '15 at 9:25
  • Something like this?: NO_VALGRIND=$(which valgrind > /dev/null || echo 1) I guess you noticed that these are not looking at the output of which or cpp. These examples check the return code (the parameter to the system exit() function or the return value of main().) So anywhere the return value is checked, you can reverse the sense of the comparison to 'invert' the result. I hope that answers your question. – RobertL Oct 27 '15 at 9:36

Your Answer

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