Bash scripting basics
Shebang, variables, quoting, arguments, reading input, arithmetic and exit codes.
A bash script is just a text file of commands you'd otherwise type by hand: the same commands, saved, repeatable and shareable. Scripts are how sysadmins and developers automate backups, deployments, setup steps and reports. In this lesson you'll write your first scripts and learn the building blocks: the shebang, variables, quoting, arguments, input, arithmetic and exit codes.
Your first script#
Three things make this work:
- The shebang
#!/usr/bin/env bashon the first line tells the kernel which interpreter runs the file.#!/bin/bashis also common;envfinds bash on yourPATH, which is more portable (macOS, BSD, NixOS). - The execute permission from
chmod +x. - A path to run it:
./hello.sh. The current directory isn't on yourPATH(for security), so you must say./. Without+xyou can still runbash hello.sh.
Lines starting with # are comments. Put personal scripts in ~/.local/bin (on PATH by default on Ubuntu once it exists) or ~/bin to run them from anywhere by name.
💡 Don't name scripts after existing commands (
test,install,time), or you'll get confusing results depending on which onePATHfinds first.
Variables#
Rules to remember:
- No spaces around
=.name = "Ada"runs a command callednamewith arguments=andAda. - By convention, lowercase names are for your own variables; UPPERCASE is for environment variables (
PATH,HOME). - Undefined variables expand to an empty string, silently. (In the robust-scripts lesson,
set -uwill turn that into an error.) - Store command output with command substitution:
today=$(date +%F).
Quoting: the most important rule#
Quote every variable expansion ("$var", "$(cmd)", "$@") unless you have a specific reason not to. This one habit prevents most bash bugs, including some catastrophic ones like rm -rf $dir/ with an empty $dir.
Script arguments#
When you run ./script.sh one two three, bash fills in special variables:
Note that "banana split" stays one argument because it was quoted on the command line and because the script uses "$@".
Default values
Parameter expansion gives defaults without any if:
${1:?Usage: greet.sh NAME} instead would stop the script with that error message if $1 were missing.
Reading input#
read reads a line from stdin into variables:
(Prompts aren't shown when input comes from a pipe instead of a terminal.) Always use -r so backslashes are kept literally. ${#var} gives a string's length.
Arithmetic#
Bash variables are strings, but $(( )) does integer maths:
Integer division truncates (17 / 5 = 3). Inside $(( )) you don't need $ before variable names.
Useful string tricks#
basename and dirname commands do the same as the first two, but parameter expansion is faster and built in.
Exit codes#
Every command returns an exit status: 0 for success, 1–255 for failure. Your script returns the status of its last command, or whatever you pass to exit:
(We'll cover if and [ ] properly in the next lesson.) Good scripts:
- print errors to stderr with
>&2, so they aren't mixed into output that might be piped elsewhere; - exit non-zero on failure, so callers (
&&, cron, CI pipelines, systemd) know something went wrong; - print a usage message when called incorrectly.
A small but real script#
Putting it together, here's a backup script that archives a directory with a timestamp:
(The sed at the end just hides the changing timestamp for this example.) Notice that the directory name contains a space, and the script still works because every expansion is quoted.
Common mistakes#
- Spaces around
=in assignments:x = 5is a command, not an assignment. - Unquoted variables. Values with spaces or
*break your script. - Windows line endings. A script edited on Windows fails with
/usr/bin/env: 'bash\r': No such file or directory. Fix it withsed -i 's/\r$//' script.shordos2unix. - Forgetting
chmod +xor the./prefix. - Writing
sh script.shfor a bash script. On Debian/Ubuntushisdash, which lacks bash features like[[ ]], arrays and{1..5}. - Using
$*instead of"$@"when passing arguments on.
What's next#
Scripts get really useful when they make decisions and repeat work. Next: conditionals and loops, with if, [[ ]], case, for and while.
Check your understanding
Quick quiz
1.Why should you write
"$file"(with quotes) instead of$file?2.In a script run as
./deploy.sh prod 3, what is$2?3.What does
exit 1at the end of a script signal to the caller?
Finished reading?
Mark this lesson complete to track your progress.