Meaning of $? (dollar question mark) in shell scripts
Asked Answered
E

8

342

What does

echo $?

mean in shell programming?

Escape answered 30/8, 2011 at 18:33 Comment(2)
exit status of last commandWomack
This answer is correct, but also note that in CSH, $?VARIABLE_NAME has a different meaning, for checking if a variable is set.Loiseloiter
Y
372

This is the exit status of the last executed command.

For example the command true always returns a status of 0 and false always returns a status of 1:

true
echo $? # echoes 0
false
echo $? # echoes 1

From the manual: (acessible by calling man bash in your shell)

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

By convention an exit status of 0 means success, and non-zero return status means failure. Learn more about exit statuses on wikipedia.

There are other special variables like this, as you can see on this online manual: https://www.gnu.org/s/bash/manual/bash.html#Special-Parameters

Yourself answered 30/8, 2011 at 18:35 Comment(0)
M
35

$? returns the exit value of the last executed command. echo $? prints that value on console. zero implies a successful execution while non-zero values are mapped to various reason for failure.

Hence when scripting; I tend to use the following syntax

if [ $? -eq 0 ]; then
 # do something
else
 # do something else
fi

The comparison is to be done on equals to 0 or not equals 0.

** Update Based on the comment: Ideally, you should not use the above code block for comparison, refer to @tripleee comments and explanation.

Molybdate answered 16/12, 2014 at 9:21 Comment(8)
No, this is an antipattern. Anything which looks like cmd; if [ $? -eq 0 ]; then should be refactored to if cmd; then. The very purpose of if (and the other flow control statements in the shell) is to run a command and examine its exit status.Replicate
if cmd; may not be very readable is some conditions especially when cmd refers to another script.Molybdate
This is even more wrong now. [ 1 ] and [ 0 ] are both true; [ without an operator checks if the argument is a non-empty string.Replicate
Why would externalscript; if [ $? -eq 0 ] be more readable than if externalscript?Replicate
I'm about to do vendor/bin/drush status bootstrap | grep -q $(vendor/bin/drush php-eval 'if (function_exists("t")) echo t("Successful");') &> /dev/null;. If I had to put that in a single line if [ ... ] it would be terribly unreadable. I plan to store the output of that line to a variable so I can just say if [ $drupal_installed -eq 0 ] later.Hamitic
See also Why is testing “$?” to see if a command succeeded or not, an anti-pattern?Replicate
@Hamitic The proper solution to that is to encapsulate the complex test in a shell function.Replicate
I would say if there are only 0 and non-0, it's ok to check if cmd, if I need to act according to more than two cases, I need to check against, for example, 0 and 1 and 2.Theodolite
O
17

echo $? - Gives the EXIT STATUS of the most recently executed command . This EXIT STATUS would most probably be a number with ZERO implying Success and any NON-ZERO value indicating Failure

? - This is one special parameter/variable in bash.

$? - It gives the value stored in the variable "?".

Some similar special parameters in BASH are 1,2,*,# ( Normally seen in echo command as $1 ,$2 , $* , $# , etc., ) .

Outbreak answered 19/10, 2017 at 19:18 Comment(0)
C
9

It has the last status code (exit value) of a command.

Crescin answered 30/8, 2011 at 18:35 Comment(0)
T
8

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:
     * https://mcmap.net/q/13911/-why-does-wait-set-status-to-255-instead-of-the-1-exit-status-of-the-forked-process
     **/
    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

Tympany answered 1/8, 2019 at 7:34 Comment(1)
See also stackoverflow.com/questions/36313216/…Replicate
R
3

From http://www.gnu.org/s/bash/manual/bash.html#Special-Parameters

?
Expands to the exit status of the most recently executed foreground pipeline. 
Reincarnation answered 30/8, 2011 at 18:36 Comment(0)
P
3

See The Bash Manual under 3.4.2 Special Parameters:

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

It is a little hard to find because it is not listed as $? (the variable name is "just" ?). Also see the exit status section, of course ;-)

Happy coding.

Placebo answered 30/8, 2011 at 18:39 Comment(0)
L
2

Outputs the result of the last executed unix command

0 implies true
1 implies false
Lynd answered 17/3, 2013 at 7:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.