Bash Scripts: Variables & Quoting

Linux · Lesson 27 of 32 7 min read Updated

A Bash script is a text file full of commands that Bash runs one after another, exactly as if you had typed them. Anything you repeat, such as a backup, a deployment or a daily report, can become a script, so it runs the same way every time, even when you are tired or someone else runs it.

This lesson starts the scripting section of the course. You will write and run a script, then learn the building blocks: variables, the quoting rules that trip up almost everyone, command substitution, arithmetic, arrays and Bash's handy parameter tricks. All examples run in Bash 5.3.

Your first script

Create a file called hello.sh with nano:

Terminal
#!/usr/bin/env bash
# hello.sh: my first script
name="Priya"
city="Jaipur"
echo "Namaste, $name from $city!"
echo "Today is $(date +%A). Logged in as $(whoami)."

The first line is the shebang: #! followed by the program that should run the file. #!/usr/bin/env bash finds bash through PATH, which works on more systems; #!/bin/bash is also very common on Linux. Lines starting with # are comments, notes for humans that Bash ignores. Now make the file executable and run it:

Terminal
chmod +x hello.sh
./hello.sh
bash hello.sh
Output
Namaste, Priya from Jaipur!
Today is Friday. Logged in as priya.
Namaste, Priya from Jaipur!
Today is Friday. Logged in as priya.

./hello.sh means "run hello.sh from the current folder"; the ./ is needed because the current folder is not in PATH. Without chmod +x you get "Permission denied". bash hello.sh runs the file through Bash directly and does not need the execute permission. The .sh extension is only a convention; Linux uses the shebang, not the name.

Variables

Assign with name=value and no spaces around =. Read the value with $name, or ${name} when text follows straight after it. All Bash variables hold text; there are no separate number or string types.

Terminal
item="Kaju Katli"
weight=2
echo "Item: $item"
echo "Packed ${weight}kg of $item"
readonly SHOP="Sharma Sweets"
Output
Item: Kaju Katli
Packed 2kg of Kaju Katli

readonly makes a constant that cannot be changed later in the script. Use UPPER_CASE for constants and exported settings, and lower_case for ordinary variables, so you never accidentally overwrite important environment variables such as PATH or USER.

Quoting: the rule that prevents most bugs

Bash treats three kinds of quoting differently:

FormEffectExample result
"double"Variables and $(...) are expanded; spaces are kept"Item: $item" gives Item: Kaju Katli
'single'Everything is literal, nothing is expanded'Item: $item' gives Item: $item
\Escapes one character"Price: \$12" gives Price: $12

When a variable is used without quotes, Bash splits its value on spaces and expands wildcards in it. That breaks as soon as a value contains a space:

Terminal
file="exam marks.txt"
cat $file
cat "$file"
Output
cat: exam: No such file or directory
cat: marks.txt: No such file or directory
92

Unquoted, $file became two arguments, exam and marks.txt. The rule is simple: always put variables in double quotes, "$file", unless you have a specific reason not to. The tool ShellCheck (sudo apt install shellcheck, then shellcheck script.sh) warns you about every unquoted variable and many other mistakes.

Command substitution and arithmetic

$(command) runs a command and inserts its output. $(( expression )) does whole-number arithmetic:

Terminal
today=$(date +%F)
files=$(ls /etc | wc -l)
price=450
qty=3
total=$(( price * qty ))
echo "$today: $files entries in /etc, bill Rs $total"
echo $(( 7 / 2 )) $(( 7 % 2 )) $(( 2 ** 10 ))
(( qty++ ))
echo "qty is now $qty"
Output
2026-10-30: 208 entries in /etc, bill Rs 1350
3 1 1024
qty is now 4

Inside $(( )) you can write variable names without $. Bash arithmetic is integers only: 7 / 2 is 3, and % gives the remainder. For decimals, use awk or bc. You may see backticks, as in today=`date`, in old scripts; they mean the same as $(date) but cannot be nested easily, so use $( ).

To print formatted output, prefer printf over echo. It works like C's printf and behaves the same everywhere: printf "%-10s %6.2f\n" "Ladoo" 12.5 prints Ladoo 12.50.

Reading input

read reads one line from the keyboard (or a pipe) into a variable. -p shows a prompt and -r stops backslashes being treated specially, so always use it:

Terminal
read -r -p "Your name: " uname
echo "Hello, $uname"
Output
Your name: Kabir
Hello, Kabir

Scripts that run unattended (from cron or systemd) should take arguments instead of asking questions; you will learn arguments in Functions, Arguments & Exit Codes.

Parameter expansion

Bash can transform a variable's value as it reads it, with no extra programs:

Terminal
file="/var/www/shop/report-2026.csv"
echo "${file##*/}"
echo "${file%.csv}.bak"
echo "${#file}"
name="kabir"
echo "${name^} ${name^^}"
echo "${backup_dir:-/srv/backups}"
Output
report-2026.csv
/var/www/shop/report-2026.bak
29
Kabir KABIR
/srv/backups

##*/ removes everything up to the last / (the file name), %.csv removes a suffix, ${#var} is the length, ^ and ^^ capitalise, and :- supplies a default when the variable is empty or unset, which is perfect for optional settings.

Arrays

An array holds a list of values. Quote "${array[@]}" to get each element as a separate word, even if it contains spaces:

Terminal
cities=(Pune Jaipur "Navi Mumbai")
cities+=(Kochi)
echo "First: ${cities[0]}, count: ${#cities[@]}"
for c in "${cities[@]}"; do echo "- $c"; done
Output
First: Pune, count: 4
- Pune
- Jaipur
- Navi Mumbai
- Kochi

Indexes start at 0. You will meet for loops properly in the next lesson.

Common mistakes

  • Spaces around =. name = "Priya" runs a command called name. Write name="Priya".
  • Unquoted variables. rm $file with file="my notes.txt" tries to delete my and notes.txt. Write rm "$file".
  • Single quotes when you wanted expansion. 'Hello $name' prints the dollar sign literally.
  • Windows line endings. A script saved in Notepad may fail with $'\r': command not found. Convert it with sed -i 's/\r$//' script.sh, or save with LF endings in your editor.
  • Forgetting ./. Typing hello.sh says "command not found" because the current folder is not in PATH.

Key points

  • A script starts with a shebang such as #!/usr/bin/env bash; make it executable with chmod +x and run it with ./script.sh.
  • Variables: name=value (no spaces), read with "$name" or "${name}".
  • Double quotes expand variables, single quotes keep text literal; quote every variable.
  • $(command) inserts command output; $(( )) does integer arithmetic.
  • ${var:-default}, ${var%suffix}, ${var##*/} and ${#var} transform values without extra tools.

Exercise

Write a script bill.sh that asks for a customer name, an item and a quantity, uses a fixed price of Rs 350 per item, and prints a bill like Bill for Ananya: 3 x Barfi = Rs 1050 (2026-10-30). Make the customer name start with a capital letter even if typed in lower case.

Show solution
Terminal
#!/usr/bin/env bash
# bill.sh: print a simple sweet-shop bill
readonly PRICE=350
read -r -p "Customer: " customer
read -r -p "Item: " item
read -r -p "Quantity: " qty
total=$(( qty * PRICE ))
echo "Bill for ${customer^}: $qty x $item = Rs $total ($(date +%F))"
Output
Customer: ananya
Item: Barfi
Quantity: 3
Bill for Ananya: 3 x Barfi = Rs 1050 (2026-10-30)

readonly protects the price from being changed by mistake, $(( )) does the multiplication, and ${customer^} capitalises the first letter. Every variable is inside double quotes or arithmetic, so names with spaces such as "Navi Mumbai" would also work.

Quick Quiz

1. What does echo 'Total: $total' print?

2. Why should you write "$file" instead of $file?

3. What is the result of echo $(( 17 / 5 )) in Bash?

4. With f="photo.jpg", what does echo "${f%.jpg}.png" print?

Learn Linux with a mentor

Free lessons get you started. Our Ethical Hacking & Cyber Security training adds real projects and code reviews from working developers.

  • Project-based learning
  • Mentor code reviews
  • Free counselling call