Trying to Understand Behavior of `Test ` in Bash

Suppose I have this simple C program (test.c):

#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv) {
     exit (1);
}

Obviously, the exit code of this program is 1:

$ gcc test.c
$ ./a.out
$ echo $?
1

But when I run test ./a.out, the result of the test doesn't match the exit code:

$ test ./a.out
$ echo $?
0

So what is actually being tested? Why is the result of the test 0?

0

2 Answers

test is a Bash built-in, often invoked by the alternative name [.

The last command (test ./a.out) exits with status 0 indicating success because test ./a.out checks whether ./a.out as a string has one or more characters in it (is not an empty string), and because it isn't an empty string, returns success or 0. The test ./a.out command line does not execute your a.out program — as you could see by printing something from within your program.

As written, your program doesn't need the <stdio.h> header or the arguments to main() — it should be int main(void). You could lose <stdlib.h> too if you use return 1; instead of exit(1);:

int main(void)
{
    return 1;
}

To use the exit status in an if condition in the shell, just use it directly:

if ./a.out ; then
    echo Success
else
    echo Failure
fi

Rule of Thumb: Don't call C programs test because you will be confused sooner or later — usually sooner rather than later.

3
  1. Your C program returns "1" to the shell (I'd prefer"return()" over exit()", but...)

  2. If you wanted to actually run "a.out" in conjunction with the "*nix" test command, you'd use syntax like:

    `./a.out` # classic *nix
    

    or

    $(./a.out) # Bash
    
  3. If you did that, however, "test" would read the value printed to "stdout", and NOT the value returned by your program on exit.

  4. You can read more about test here:

  5. Here is an example:

C program:

#include <stdio.h>

int main (int argc, char *argv[]) {
  printf("%d\n", argc);
  return 2;
}

Shell script:

echo "Assign RETVAL the return value of a.out:"
./a.out RETVAL=$? echo " " RETVAL=$RETVAL

echo "Assign RETVAL the value printed to stdout by a.out:"
RETVAL=$(./a.out) echo " " RETVAL=$RETVAL

echo "Turn an 'trace' and run a.out with 'test':" 
set -x 
if [ $(./a.out) -eq 1 ]; then
  echo "One" 
else
  echo "Not One"
fi

Example output:

paulsm@vps2:~$ ./tmp.sh
Assign RETVAL the return value of a.out:
1
  RETVAL=2
Assign RETVAL the value printed to stdout by a.out:
  RETVAL=1
Turn an 'trace' and run a.out with 'test':
+++ ./a.out
++ '[' 1 -eq 1 ']'
++ echo One
One

ALSO:

A couple of points that have already been mentioned:

a. return 1 is generally a better choice than exit (1).

b. "test" is probably a poor name for your executable - because it collides with the built-in "test" command. Something like "test_return" might be a better choice.

0

Your Answer

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

Chloe Bennett

Chloe Bennett

Culture, Media & Entertainment Columnist

Chloe Bennett explores the intersection of pop culture, streaming entertainment, digital trends, and contemporary lifestyle. Her weekly commentary reaches thousands of culture enthusiasts.

Share this article
Twitter Facebook Pinterest