Exit codes and statuses

Everytime a command is executed it returns an exit code. All the following names refer the same thing: return status,exit status or exit code.
Exit code value is an integer, ranging from 0 to 255.
By convention, successfully executed command return a exit code 0. A non-zero exit code is returned if some kind of error/failure is encountered.
These exit codes can be used for error checking in your scripts. Tests can be simple, like; was the exit code 0, or more complex, like; a specific error code.

If you want find out what the various exit codes a command can return, you have to manually test them or take a look at the manual of the command or it's source code. Try out a man or info commands combined with a desired command to read the documention (usually not mentioned).
As an example; grep commands documention explains the following: > Normally the exit status is 0 if a line is selected, 1 if no lines were selected, and 2 if an error occurred. However, if the -q or --quiet or --silent is used and a line is selected, the exit status is 0 even if an error occurred.

Just remember the basics: 0 for success, non-zero for some sort of error.

The special variable $? contains the exit status of the last command executed.
And with a functions, $? gives the exit status of the last command executed in the function, unless a return is not manually defined.

In the following example, ls command is called with a file path that doesnt exist.

#!/bin/bash

ls /path/not/available #invalid file path
echo ${?} #print exit code

#output:
#ls: /path/not/available: No such file or directory
#2

Exit command

Like you allready know, normal commands and shell scripts return an exit code. When the exit command is reached, shell script will stop running. You can control the exit code of your shell script by using a exit command. Simply use the exit command followed by a integer 0 to 255.
If no "manual" exit is not specified, the latest commands exit code is used.

ls $HOME #command will succeed
exit 100 # return your own exit code

#echo $1 #test it in you shell

Exit codes can be used to make decisions or perform a different actions based on the exit code.

In the following example, script tries to create a new folder and command's return code is checked. If the command returns 0, 0 is returned from the script, if the command returns a non-zero value, script returns exit code 1 and exits.

#!/bin/bash

read -p "give a new folder: " FOLDER
mkdir ${FOLDER}

#If mkdir command fails, command returns exit code 1
#In that case the if stament becames true, and the block inside of it will be executed
#Exit 1 line stops the execution of the script and exit code 1 is returned
#Test this on you shell after the script execution: echo $?

if [ ${?} -ne 0 ]
then
  echo "cannot create a folder: ${FOLDER}"
  exit 1
fi

exit 0

If you dont't like to use a special variable, you can assign the return code of the command to a other variable and then use that variable in you script.
just remember to assign a value to a variable immediatly after the command!

#!/bin/bash

read -p "give a new folder: " FOLDER
mkdir ${FOLDER}
EXIT_CODE=$?

if [ ${EXIT_CODE} -ne 0 ]
then
  echo "cannot create a folder: ${FOLDER}"
  exit 1
fi

exit 0

Exit codes and a logical operators?

In addition to using if staments to analyse exit statuses, logical ANDs (&&) and ORs (||) can be used.

A command that is following a AND operator, will only execute if the previous command succeeds (returns 0).

#!/bin/bash

mkdir /testfolder && ls /testfolder #The second command will not be executed because folder cannot be created

#output:
#mkdir: cannot create directory ‘/testfolder’: Permission denied

A command that following a OR operator, will only execute if the previous command fails (returns non-zero code).

#!/bin/bash

mkdir /home/user/testfolder || mkdir /tmp/testfolder #The first command succeeds, no need to execute the second one
#output: /home/user/testfolder was created

#or the other way around

mkdir /testfolder || mkdir /tmp/testfolder #The first command fails (returns non-zero), try the second one
#output: /tmp/testfolder was created

If you want to chain commands together in a single line without exit code checking, use a semicolon (;) between commands. That way the commands will be always get executed, no matter if the previous command failed or succeeded.

#!/bin/bash

#earlier 'and' example
mkdir /testfolder ; ls /testfolder

#Is the same as

mkdir /testfolder
ls /testfolder

#Output for both styles will be:
#mkdir: cannot create directory ‘/testfolder’: Permission denied
#ls: cannot access '/testfolder': No such file or directory