Linux and Bash course menu
Bash Script Arguments and Exit Codes Explained
Read script arguments with $1, $@ and $#, parse options with getopts, write usage messages, set exit codes, and check them with $? in Bash.
On this page
Arguments#
Values typed after a script's name arrive as $1, $2 and so on:
cat > args.sh <<'EOF'
#!/bin/bash
echo "Script: $0"
echo "First: $1"
echo "Second: $2"
echo "Count: $#"
echo "All: $*"
EOF
bash args.sh apple "green mango"Script: args.sh
First: apple
Second: green mango
Count: 2
All: apple green mangoCheck the arguments#
A good script explains itself if it is used wrongly:
cat > backup.sh <<'EOF'
#!/bin/bash
if [[ $# -ne 2 ]]; then
echo "Usage: $0 <source> <destination>" >&2
exit 1
fi
echo "Copying $1 to $2"
EOF
bash backup.sh only-one
echo "Exit code: $?"
bash backup.sh docs /backupUsage: backup.sh <source> <destination>
Exit code: 1
Copying docs to /backupExit codes#
Every command finishes with a number. 0 means success, and anything else means failure. $? holds the last one:
ls /etc/hostname > /dev/null
echo "$?"
ls /nothing 2> /dev/null
echo "$?"0
2Scripts set theirs with exit. Without it, a script returns the status of its last command.
CI systems, cron and other scripts all use exit codes to tell whether something worked, so always exit with non-zero on failure.
Shift through arguments#
shift drops $1 and moves the rest down:
sum() {
local total=0
while (( $# > 0 )); do
((total += $1))
shift
done
echo "$total"
}
sum 5 10 2035Options with getopts#
getopts handles flags such as -v and -o file. A colon after a letter means that it takes a value:
cat > report.sh <<'EOF'
#!/bin/bash
verbose=false
output="report.txt"
while getopts "vo:h" opt; do
case "$opt" in
v) verbose=true ;;
o) output="$OPTARG" ;;
h) echo "Usage: $0 [-v] [-o file] name"; exit 0 ;;
*) echo "Try -h for help" >&2; exit 2 ;;
esac
done
shift $((OPTIND - 1))
echo "verbose=$verbose output=$output name=$1"
EOF
bash report.sh -v -o june.txt sales
bash report.sh sales
bash report.sh -x sales
echo "Exit code: $?"verbose=true output=june.txt name=sales
verbose=false output=report.txt name=sales
report.sh: illegal option -- x
Try -h for help
Exit code: 2shift $((OPTIND - 1)) removes the options, leaving the normal arguments.
Long options#
getopts handles only single letters. For --verbose, use a case in a while loop:
cat > long.sh <<'EOF'
#!/bin/bash
while (( $# > 0 )); do
case "$1" in
--name) name="$2"; shift 2 ;;
--force) force=yes; shift ;;
*) echo "Unknown option: $1" >&2; exit 2 ;;
esac
done
echo "name=$name force=${force:-no}"
EOF
bash long.sh --name asha --forcename=asha force=yesCommon mistakes#
Practice#
Practice exercise
Write count.sh, which takes a file name, prints the number of lines, and exits with code 1 and a message on stderr if the file does not exist. Test both cases.
Show solution
cat > count.sh <<'EOF'
#!/bin/bash
file="$1"
if [[ ! -f "$file" ]]; then
echo "No such file: $file" >&2
exit 1
fi
wc -l < "$file"
EOF
bash count.sh /etc/hostname
bash count.sh /nothing
echo "Exit code: $?"1
No such file: /nothing
Exit code: 1Key takeaway#
Next: Arrays and String Handling.
Finished this lesson?