Skip to content

Day 6 · Shell Scripting I — Fundamentals

Days 6–7 are your gentle on-ramp to programming with the shell — no prior coding needed. Today (the first half) is the toolkit every script is built from: writing and running a file, printing, variables, quoting, input, math, and reporting success. Tomorrow (the second half) teaches scripts to make decisions, repeat work, and organize into functions. Each idea builds on the last.

Learning Objectives

  • Write, save, and run a script from the shebang up
  • Comment your code and print cleanly with echo and printf
  • Store values in variables and quote them correctly
  • Read user input and do arithmetic
  • Capture a command's output with command substitution
  • Report success or failure with exit codes

Theory · ~20 min

1. A script is just a file of commands

A script is a plain text file of commands, run top to bottom — type once, run forever. The first line is the shebang: it tells the system which interpreter runs the file.

#!/bin/bash
# lines starting with # are COMMENTS — notes for humans, ignored by bash
echo "This is my first script"

Make it executable (Day 4 permissions!) and run it:

chmod +x myscript.sh     # grant the execute bit
./myscript.sh            # run it  ( ./ = "the file right here" )

Why ./?

Without ./, the shell only searches your PATH (Day 5) — and the current folder isn't on it. ./myscript.sh says run this exact file.

2. Printing — echo and printf

echo "Hello, world"
printf "%s is %d years old\n" "Sam" 42    # %s = string, %d = number, \n = newline

echo is the everyday tool; printf gives precise, repeatable formatting — reach for it when output needs to line up or follow an exact shape.

3. Variables — store a value

name="Sam"          # NO spaces around =
count=5

No spaces around =

name = "Sam" fails — bash reads name as a command. It must be name="Sam".

Read a variable back with $. Braces ${...} keep the name separate from surrounding text:

echo "Hello, $name"
file="report"
echo "${file}.txt"        # report.txt  (without braces, bash looks for $filetxt)

4. Quoting — the habit that prevents most bugs

Quoting decides whether the shell expands what's inside. This is the single biggest source of beginner scripting bugs, so learn it now:

Quotes $var expands? Use for
"double" yes almost everything — text containing variables
'single' no — fully literal exact text, or when you mean a literal $
none yes, and splits on spaces avoid around values
name="Sam Smith"
echo "$name"        # Sam Smith          ← safe
echo '$name'        # $name              ← literal, no expansion
echo $name          # Sam Smith  BUT passed as TWO words — breaks on spaces & globs

Rule of thumb

Always wrap variables in double quotes"$name", "$@", "$(cmd)" — unless you have a specific reason not to.

5. Defaults with parameter expansion

${var:-default} substitutes default when var is unset or empty, so a script doesn't break on missing input. ${#var} gives its length:

echo "Deploying to ${ENVIRONMENT:-staging}"   # 'staging' when ENVIRONMENT is unset
echo "Name is ${#name} characters"            # length of $name

6. Reading input

read -rp "What is your name? " name
echo "Nice to meet you, $name"

-p shows the prompt; -r keeps backslashes literal (always include it).

7. Arithmetic

Integer math goes inside $(( ... )):

a=6; b=4
echo $(( a + b ))     # 10
echo $(( a * b ))     # 24
echo $(( a / b ))     # 1   (integer division)

Shells do integer math only

$(( 7 / 2 )) is 3, not 3.5. For decimals you'd reach for bc or awk (see Advanced Topics).

8. Command substitution — capture output into a value

$( ... ) runs a command and hands back its output:

today=$(date +%F)
echo "Today is $today"
count=$(ls | wc -l)
echo "$count items here"

This is how scripts pull live data from the system.

9. Exit codes — reporting success or failure

Day 5 showed every command returns an exit code ($?): 0 = success, non-zero = failure. Your script sets its own with exit:

mkdir -p ~/backups || exit 1     # bail out (exit 1) if mkdir failed — Day 5's ||
echo "backup dir ready"
exit 0                           # 0 = success (also the default at the end)

Send errors to stderr

Print error messages with >&2 so they land on stderr (Day 5), not mixed into real output: echo "something broke" >&2.


Lab · ~50 min

Work inside your Vagrant VM. Keep scripts in ~/scripts:

mkdir -p ~/scripts && cd ~/scripts

Step 1 — Your first script

cat > first.sh << 'EOF'
#!/bin/bash
# my very first script
echo "Hello from my first script!"
EOF

chmod +x first.sh
./first.sh

Step 2 — Variables and braces

cat > vars.sh << 'EOF'
#!/bin/bash
name="DevOps Student"
week=1
echo "Hello, $name"
echo "You are in week ${week}"
EOF

chmod +x vars.sh
./vars.sh

Step 3 — Quoting in action

See the three quoting styles diverge with your own eyes:

cat > quotes.sh << 'EOF'
#!/bin/bash
who="Sam Smith"
echo "double: $who"     # expands
echo 'single: $who'     # literal
echo naked:  $who       # expands AND splits — watch the spacing collapse
EOF

chmod +x quotes.sh
./quotes.sh

Step 4 — Read input and do math

cat > calc.sh << 'EOF'
#!/bin/bash
read -rp "First number:  " a
read -rp "Second number: " b
echo "Sum:     $(( a + b ))"
echo "Product: $(( a * b ))"
EOF

chmod +x calc.sh
./calc.sh

Step 5 — Command substitution: a system snapshot

cat > snapshot.sh << 'EOF'
#!/bin/bash
user=$(whoami)
host=$(hostname)
today=$(date +%F)
items=$(ls "$HOME" | wc -l)
printf "%s@%s — %s — %d items in home\n" "$user" "$host" "$today" "$items"
EOF

chmod +x snapshot.sh
./snapshot.sh

Step 6 — Defaults and an exit code

cat > backupdir.sh << 'EOF'
#!/bin/bash
# create a backup dir named by today's date, with a sensible default location
base="${1:-$HOME/backups}"
dir="$base/$(date +%F)"

mkdir -p "$dir" || { echo "could not create $dir" >&2; exit 1; }
echo "ready: $dir"
exit 0
EOF

chmod +x backupdir.sh
./backupdir.sh                 # uses the default ~/backups
./backupdir.sh /tmp/mybackups  # uses the path you pass
echo "exit code: $?"

Notice how each script adds exactly one new idea on top of the last — small, composable steps.


Advanced Topics

You have everything you need to write useful scripts. When you want to go deeper, these are the next rungs — read the linked resource for each:


Assignment

Three short scripts, each drilling one core skill — the kind of "gather and report" work you do every time you log into an unfamiliar server. They use only today's tools.

  1. sysinfo.sh — the first-login snapshot (command substitution + quoting). Print a clean report of who and where you are: hostname, the user running the script, kernel version (uname -r), uptime (uptime -p), and the current date & time. Format it with printf, quote every variable, and start the file with a comment header.

  2. meminfo.sh — memory used % (arithmetic). From free -m, read total and used RAM and compute the used percentage yourself with $(( )). Hints: free -m | awk 'NR==2 {print $2, $3}' gives you total and used to capture with command substitution; and since shells do integer math, used * 100 / total (multiply first) — used / total * 100 would give 0.

  3. diskusage.sh — inspect any directory (parameter defaults + exit codes). Take a directory as $1, defaulting to $HOME (${1:-$HOME}). Print its total size (du -sh) and the disk space used on / (df -h /). If du fails on the path, print an error to stderr and exit 1 (use Day 5's ||).

Run every script through ShellCheck before you consider it done.


Further Reading