Skip to main content

Lesson: Your First Bash Scripts

What you'll learn

  • What a Bash script actually is, and how the "shebang" line tells Linux how to run it.
  • How to make a script executable with chmod +x and the difference between ./script and bash script.
  • How to store data in variables and use them safely with quoting.
  • How to capture the output of a command into a variable with command substitution $( ).

By the end you can turn a sequence of commands you already type by hand into a small, repeatable script you run with one command.

The lesson

A shell script is just a plain text file containing the same commands you would type into your terminal, saved so you can run them all at once. Instead of typing five commands every morning, you put them in a file and run that file. That is the whole idea of automation: do it once, save it, reuse it forever.

You will write and run every script in this module on the Jumpbox (10.100.100.254, Ubuntu, user ubuntu). Log in there first.

1. The shebang line

The very first line of a script should be a shebang (pronounced "shuh-bang"). It tells the operating system which program should interpret the file:

#!/usr/bin/env bash

The #! characters are the shebang marker. /usr/bin/env bash means "find bash on the system's PATH and run this file with it." This is more portable than hard-coding #!/bin/bash, because Bash lives in different places on different systems, but env is almost always at /usr/bin/env.

Create your first script with a text editor like nano:

nano hello.sh

Type this in:

#!/usr/bin/env bash
echo "Hello from the Jumpbox!"

Save and exit (Ctrl+O, Enter, Ctrl+X in nano).

2. Making it executable and running it

A fresh file is not executable yet. Check its permissions:

ls -l hello.sh
# -rw-rw-r-- 1 ubuntu ubuntu 50 Jun  1 10:00 hello.sh

There is no x (execute) bit. Add it with chmod ("change mode"):

chmod +x hello.sh
ls -l hello.sh
# -rwxrwxr-x 1 ubuntu ubuntu 50 Jun  1 10:00 hello.sh

Now there are three ways the script can run. Here is what happens in each case:

  ./hello.sh        -> kernel reads the shebang, runs: bash hello.sh
                       (REQUIRES the execute bit)

  bash hello.sh     -> you explicitly hand the file to bash
                       (shebang ignored, execute bit NOT needed)

  hello.sh          -> "command not found" UNLESS the dir is on $PATH

Run it the normal way:

./hello.sh
# Hello from the Jumpbox!

Why the ./? The current directory is not on your PATH (the list of folders the shell searches for commands), for safety. So you tell the shell exactly where the file is: . means "here", and /hello.sh is the file in here.

bash hello.sh is handy while debugging because it does not need the execute bit and lets you add flags like bash -x hello.sh to trace execution.

3. Variables

A variable is a named box that holds a value. You assign with = and no spaces around it:

name="Ada"
count=5

name = "Ada" (with spaces) is an error — the shell would try to run a command called name. To read a variable back, put a $ in front:

echo "$name"      # Ada
echo "$count"     # 5

Variable names are case-sensitive and conventionally lowercase for your own variables, UPPERCASE for environment variables like HOME or PATH.

4. Quoting basics

Quoting is the single biggest source of bugs for beginners, so learn it now.

  • Double quotes "..." keep text together but still expand variables and command substitution. Almost always what you want around a variable.
  • Single quotes '...' are literal: nothing inside is expanded.
  • No quotes lets the shell split the value on spaces and expand wildcards — usually a bug.

See the difference:

greeting="hello   world"
echo "$greeting"   # hello   world   (spaces preserved)
echo $greeting     # hello world     (collapsed to one space! word-split)
echo '$greeting'   # $greeting       (single quotes: literal)

The rule of thumb: always wrap your variable references in double quotes unless you have a specific reason not to. echo "$name", not echo $name.

5. Command substitution $( )

You can capture the output of a command and store it in a variable using $( ):

today=$(date +%F)
echo "Today is $today"      # Today is 2026-06-01

files=$(ls | wc -l)
echo "There are $files items here"

$(date +%F) runs date +%F, grabs what it prints, and substitutes it in place. You may see an older backtick syntax `date` — it does the same thing but is harder to read and cannot be nested cleanly. Prefer $( ).

Putting it together, a slightly more useful script:

#!/usr/bin/env bash
user=$(whoami)
host=$(hostname)
now=$(date +"%F %T")
echo "Report by $user on $host at $now"

Save as report.sh, chmod +x report.sh, run ./report.sh. You just automated a little status line — the seed of every monitoring script you will write later.

6. Comments and habits

Anything after a # (except the shebang) is a comment the shell ignores. Use comments to explain why, not what:

# Use UTC so logs from all hosts line up
now=$(date -u +"%F %T")

Because you will commit these scripts to Git in a later module, build clean habits now: one shebang line, comments at the top saying what the script does, lowercase variable names, and quotes around variables.

Dig deeper

Search terms

  • bash shebang usr bin env bash explained
  • chmod +x run shell script linux
  • bash variable assignment no spaces
  • bash double vs single quotes difference
  • bash command substitution dollar parentheses
  • why ./ before script name bash

Check yourself

  1. What does the shebang line #!/usr/bin/env bash do, and why is env used instead of a hard-coded path?
  2. Why must you type ./hello.sh instead of just hello.sh, and when can you skip the ./?
  3. What is wrong with the line name = "Ada", and how do you fix it?
  4. What is the practical difference between echo "$greeting" and echo $greeting?
  5. Write a line that stores today's date into a variable called today and prints it.