What is the $? (dollar question mark) variable in shell scripting? [duplicate]

I'm trying to learn shell scripting, and I need to understand someone else's code. What is the $? variable hold? I can't Google search the answer because they block punctuation characters.


$? is used to find the return value of the last executed command. Try the following in the shell:

ls somefile
echo $?

If somefile exists (regardless whether it is a file or directory), you will get the return value thrown by the ls command, which should be 0 (default "success" return value). If it doesn't exist, you should get a number other then 0. The exact number depends on the program.

For many programs you can find the numbers and their meaning in the corresponding man page. These will usually be described as "exit status" and may have their own section.


That is the exit status of the last executed function/program/command. Refer to:

  • exit / exit status @ tldp.org
  • Special Shell Variables @ tldp.org
  • Special Characters @ tlpd.org

A return value of the previously executed process.

10.4 Getting the return value of a program

In bash, the return value of a program is stored in a special variable called $?.

This illustrates how to capture the return value of a program, I assume that the directory dada does not exist. (This was also suggested by mike)

        #!/bin/bash
        cd /dada &> /dev/null
        echo rv: $?
        cd $(pwd) &> /dev/null
        echo rv: $?

See Bash Programming Manual for more details.


Minimal POSIX C exit status example

To understand $?, you must first understand the concept of process exit status which is defined by POSIX. In Linux:

  • when a process calls the exit system call, the kernel stores the value passed to the system call (an int) even after the process dies.

    The exit system call is called by the exit() ANSI C function, and indirectly when you do return from main.

  • the process that called the exiting child process (Bash), often with fork + exec, can retrieve the exit status of the child with the wait system call

Consider the Bash code:

$ false
$ echo $?
1

The C "equivalent" is:

false.c

#include <stdlib.h> /* exit */

int main(void) {
    exit(1);
}

bash.c

#include <unistd.h> /* execl */
#include <stdlib.h> /* fork */
#include <sys/wait.h> /* wait, WEXITSTATUS */
#include <stdio.h> /* printf */

int main(void) {
    if (fork() == 0) {
        /* Call false. */
        execl("./false", "./false", (char *)NULL);
    }
    int status;
    /* Wait for a child to finish. */
    wait(&status);
    /* Status encodes multiple fields,
     * we need WEXITSTATUS to get the exit status:
     * http://stackoverflow.com/questions/3659616/returning-exit-code-from-child
     **/
    printf("$? = %d\n", WEXITSTATUS(status));
}

Compile and run:

g++ -ggdb3 -O0 -std=c++11 -Wall -Wextra -pedantic -o bash bash.c
g++ -ggdb3 -O0 -std=c++11 -Wall -Wextra -pedantic -o false false.c
./bash

Output:

$? = 1

In Bash, when you hit enter, a fork + exec + wait happens like above, and bash then sets $? to the exit status of the forked process.

Note: for built-in commands like echo, a process need not be spawned, and Bash just sets $? to 0 to simulate an external process.

Standards and documentation

POSIX 7 2.5.2 "Special Parameters" http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_05_02 :

? Expands to the decimal exit status of the most recent pipeline (see Pipelines).

man bash "Special Parameters":

The shell treats several parameters specially. These parameters may only be referenced; assignment to them is not allowed. [...]

? Expands to the exit status of the most recently executed foreground pipeline.

ANSI C and POSIX then recommend that:

  • 0 means the program was successful

  • other values: the program failed somehow.

    The exact value could indicate the type of failure.

    ANSI C does not define the meaning of any vaues, and POSIX specifies values larger than 125: What is the meaning of "POSIX"?

Bash uses exit status for if

In Bash, we often use the exit status $? implicitly to control if statements as in:

if true; then
  :
fi

where true is a program that just returns 0.

The above is equivalent to:

true
result=$?
if [ $result = 0 ]; then
  :
fi

And in:

if [ 1 = 1 ]; then
  :
fi

[ is just an program with a weird name (and Bash built-in that behaves like it), and 1 = 1 ] its arguments, see also: Difference between single and double square brackets in Bash


$? is the result (exit code) of the last executed command.