Exit Codes, Process Operators, and Subcommands

Before we jump too far into how to run commands in sequences, let's chat one second about exit codes. Whenever a process exits, it exits with an exit code. This exit code corresponds to if a process successfully completed whatever you told it to do. Sometimes this is a bit misleading because sometime programs are meant to be stopped before they complete (as some like yes will never actually complete by themselves).

A program that successfully runs and exits by itself will have an exit code of 0. Try this:

date # show current date, runs successfully
echo $? # $? corresponds to the last exit code, in this case 0
yes # hit CTRL+C to stop it
echo $? # you stopped it so it exited with a non-zero code, 130

So what do all the codes mean? Well, it depends on the program and it's not super consistent. It can be any number from 0 to 256. But here are a few good ones that are common

  • 0: means it was successful. Anything other than 0 means it failed
  • 1: a good general catch-all "there was an error"
  • 2: a bash internal error, meaning you or the program tried to use bash in an incorrect way
  • 126: Either you don't have permission or the file isn't executable
  • 127: Command not found
  • 128: The exit command itself had a problem, usually that you provided a non-integer exit code to it
  • 130: You ended the program with CTRL+C
  • 137: You ended the program with SIGKILL
  • 255: Out-of-bounds, you tried to exit with a code larger than 255

There are a few others but these are the most common ones you'll see. You'll see some programs use numbers like 5 to 100 to signify different ways the program ended but you can pretty safely ignore that. It's usually just important if it's 0 or not-0.

Okay, so why is this important? It can be useful to see if a previous command succeeded or not, but it's also useful for running programs in a sequence using operations.

So what if you need to run two processes in a row, one right after the other? Well, you have a few options.

Run if first one succeeds

You'll probably see this the most. Let's say I wanted to create a file, add the date to it, and then add my current uptime to it. (try runnning uptime, it just tells you how long your computer has been running.)

touch status.txt && date >> status.txt && uptime >> status.txt
cat status.txt

You can see it does all three commands right in a row. That's what the && operator does. It runs from left to right (touch, date, then uptime). The && operator will bail if any of those commands fails. Try this:

date && cat not-real-file.txt && echo hi # the date will display but hi won't

Since not-real-file.txt doesn't exit, it bails and hi is never echoed.

Run if first one fails

There's also a || command that will run if the first one fails.

false || echo hi # you'll see hi
false && echo hi # you won't see hi

false is a command that just returns 1 (there is a true that always returns 0) In this case, you'll see hi the first time and not the second time.

Always Run

If you need always run the second command, use a ; instead of either && or ||.

false ; true ; echo hey # you'll see hey


Sometimes you need to invoke a command within a command. Luckily bash has you covered here with the ability to run subcommands.

echo I think $(whoami) is a very cool user # I think ubuntu is very cool

The $() allows you to put bash commands inside of it that then you can use that output as part of an input to another command. In this case, we're using whoami to get your username to echo that affirming message out. Let's a more practical one. Let's say you wanted to make a job that you could run every day to output what your current uptime was. You could run this command

echo $(date +%x)$(uptime) >> log.txt

The +%x part is just saying what date of format you want, and I got that from reading date --help. So end printing something like

06/17/20 – 21:38:34 up 8:51, 1 user, load average: 0.00, 0.00, 0.00

There are far more useful logs to write but you can see here the power of subcommands. Note you can also use backticks like ` instead of $() but it's preferred to use the $() notation. Notably, you nest infinitely with $(). For more reasons, read here.