Intermediate · Automation basics: scheduling, archives and shell scripts · topic 37 of 57 · 35 min
Bash scripts: variables, quoting, arguments and exit codes
Explanation
Why scripts?
If you type the same ten commands every week, put them in a script. It runs the same way every time, anyone on the team can run it, and cron or a timer can run it at night. Bash scripts are the glue of Linux administration.
Your first script
$ cat > ~/bin/hello.sh << 'EOF'
#!/bin/bash
# hello.sh: greet the user and show the date
echo "Namaste, $USER"
echo "Today is $(date +%A)"
EOF
$ chmod +x ~/bin/hello.sh
$ ~/bin/hello.sh
Namaste, asha
Today is MondayVariables
name="Asha Rao" # NO spaces around =
count=3
echo "$name has $count tickets"
today=$(date +%F) # command substitution: the output of date
backup="/backup/etc-$today.tar.gz"
echo "Writing $backup"
echo "${name}_report" # braces separate the name from text after itname = "x" (with spaces) is an error: bash tries to run a command called name. Variables are text by default. For whole-number maths use $(( )): total=$(( count * 250 )).
Quoting rules
| Quote | What happens | Example |
|---|---|---|
| "double" | Variables and $( ) are expanded; spaces are kept | "$name" → Asha Rao |
| 'single' | Nothing is expanded; text is taken literally | '$name' → $name |
| none | Expanded, then split on spaces and wildcards expanded | $name → two words: Asha, Rao |
Arguments
#!/bin/bash
# greet.sh NAME [CITY]
echo "Script: $0"
echo "First argument: $1"
echo "Second argument: ${2:-Pune}" # use Pune if $2 is empty
echo "Number of arguments: $#"
echo "All arguments: $@"
$ ./greet.sh Ravi Chennai
Script: ./greet.sh
First argument: Ravi
Second argument: Chennai
Number of arguments: 2
All arguments: Ravi Chennairead asks the user for input: read -p "Username: " user. For scripts run by cron, prefer arguments or a config file; nobody is there to type.
Exit codes
Every command ends with an exit code (exit status), a number from 0 to 255. 0 means success; anything else means some kind of failure. $? holds the code of the last command. Your script should end with exit 0 on success and a non-zero code on failure, so cron, systemd and other scripts can tell what happened.
$ grep -q asha /etc/passwd; echo $?
0
$ grep -q nobodyhere /etc/passwd; echo $?
1
$ ls /nope; echo $?
ls: cannot access '/nope': No such file or directory
2
$ mkdir /backup && echo "created" # && runs the next one only on success
$ ping -c1 db01 || echo "db01 is down" # || runs the next one only on failureCommon mistakes
- Spaces around = in an assignment.
- Forgetting chmod +x, then getting "Permission denied". Or running a script written on Windows: "bad interpreter: /bin/bash^M" means Windows line endings; fix with sed -i "s/\r$//" script.sh.
- Using unquoted variables.
- Not setting an exit code, so a failed backup looks successful to cron.
Need help?
Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.
Try:
Watch the lesson
Bash scripts: variables, quoting, arguments and exit codes · 1/7
A script is a file of commands. The first line, the shebang, says bash runs it.
Prefer a video by a teacher? Find free videos on “bash scripting tutorial variables arguments exit codes beginners” on YouTube ↗
In real life: A user-creation script for a BPO in Gurugram
A BPO in Gurugram onboards 30 agents every Monday. The admin writes add-agent.sh NAME TEAM: it checks it got two arguments, creates the user in the team group, sets a temporary password that must be changed, and exits with 1 and a clear message if anything fails. The HR coordinator's list is turned into 30 calls of the script.
#!/bin/bash
if [ $# -ne 2 ]; then
echo "Usage: $0 NAME TEAM" >&2
exit 2
fi
useradd -G "$2" "$1" || exit 1
echo "$1:Welcome@123" | chpasswd && chage -d 0 "$1"
echo "Created $1 in $2"
exit 0Practice (0/3 done)
Which line correctly sets a variable?
name='Asha'. What does echo '$name' print?
Model a script's argument check in Python. Write check_args(args) where args is the list of arguments (like $@). The script needs exactly 2 arguments: NAME and TEAM, and NAME must not be empty. Return a tuple (exit_code, message): (2, "Usage: add-agent NAME TEAM") if the count is wrong, (1, "NAME is empty") if NAME is "", otherwise (0, "Creating NAME in TEAM") with the real values.
Quick quiz
Read more (free)
Related help
Before this, learn
This topic builds on these. If something here feels new, open them first.
Related topics
- Bash scripts: conditions, loops and safe habitsAutomation basics: scheduling, archives and shell scripts · same module
- One-time jobs with at, and systemd timersAutomation basics: scheduling, archives and shell scripts · same module
- Bash scripting, text tools and cronDevOps: Git, Docker, Kubernetes and CI/CD · Linux for DevOps
- Command-line tools with flag and osGo Programming (backend and cloud) · CLI tools and Go in cloud and DevOps
- Processes, signals and systemd servicesDevOps: Git, Docker, Kubernetes and CI/CD · Linux for DevOps
- Compiling with gccC Programming (from zero to systems) · Getting started with C
Questions people ask about this
Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.
What does $# hold in a script?
Answer: The number of arguments
$# is the argument count.
What exit code means success?
Answer: 0
0 = success.
What does "mkdir /x && echo ok" do if mkdir fails?
Answer: Does not print ok
&& runs the second command only if the first succeeds.
Why write rm "$file" instead of rm $file?
Answer: A file name with spaces stays one argument
Unquoted variables are split on spaces.
What does $(hostname) do inside a script?
Answer: Runs hostname and puts its output there
Command substitution.
FILE="monthly report.csv". Why is rm $FILE dangerous?Related
Answer: Without quotes it splits into two arguments, "monthly" and "report.csv"
Unquoted variables are split on spaces. Always write rm "$FILE".
Which shows a list of earlier commands?Related
Answer: history
history lists commands you ran; !N runs number N again.
Which loop reads a file line by line correctly?Related
Answer: while read -r l; do ... done < f
while read -r handles lines with spaces.
What exit code means success?Related
Pass the quiz (60% or more) or mark this topic as done when you are ready.
Your progress is saved on this device only.
Next steps
- 1 more topic in “Automation basics: scheduling, archives and shell scripts” before its exam: Bash scripts: conditions, loops and safe habits.
- Module 7 exam: Automation basics · 14 questions · 25 min, without help. 5 topics of this module still to finish first.
- Then the next module, Boot, recovery and containers. 57 topics left in the course before the final exam.
← Previous: Archives and compression: tar, gzip, bzip2 and xz