Learn Computing from the Experts | The Rheinwerk Computing Blog

How to Create Your First Bash Script

Written by Rheinwerk Computing | May 2, 2025 1:00:00 PM

A script is a text file in which you formulate the same commands that you enter interactively in the terminal.

 

Only two special features distinguish a script from just commands: The first line of the script must contain what’s called a shebang, and the file must be marked as an executable using chmod.

 

What Is a Shebang?

The first line of a script to be executed on Linux or macOS must start with the characters, # (“hash” or “sharp”) and ! (“bang”) as well as the path of the interpreter. “Shebang” is a linguistic shortening of “sharp bang.”

 

The interpreter is the program that reads and processes the script file, and in this chapter, that interpreter is Bash (or in exceptional cases Zsh). However, other programming languages can also be used as interpreters, such as Python or Perl.

 

On Linux, the Bash program is predominantly installed in the /bin directory. Thus, a Bash script starts with the following line:

 

#!/bin/bash

 

Common Shebang Variants

Depending on which distribution or Unix-like system you’re working in, Bash may be installed in a different directory, such as /usr/bin. Using which bash, you can determine the location on your computer as in the following example:

 

$ which bash

 

   /usr/bin/bash

 

Links often make sure that both /usr/bin/bash and /bin/bash work. A general-purpose hash bang formulation that works regardless of the location uses the /usr/bin/env command. This command searches all common locations:

 

#!/usr/bin/env bash

 

If Bash is not installed on your machine at all and you’re using Zsh, you’ll want to replace /bin/bash with /bin/zsh (or the location where Zsh is installed on your machine):

 

#!/bin/zsh

 

If you want your script to work regardless of whether Bash or Zsh is available, the following hash bang will get you there:

 

#!/bin/sh

 

sh is a link that points to the default shell on most Linux systems. However, you must be aware that different shells will be used from now on when your script is run, depending on the computer/distribution. For example, on Debian and Ubuntu, the speed-optimized Dash will be executed. When formulating your scripts, you must be careful not to use any Bash-specific extensions that are not available in other shells.

 

Unless there are compelling reasons not to, you should always use #!/bin/bash as the hash bang.

 

Why chmod Is Needed

All Unix-like systems including Linux and macOS store some access bits along with each file that provide information about who is allowed to read, write, and execute the file. For scripts, the execute bit (x for short) is crucial. Only with this bit can a script be executed later like a command.

 

To set the execute bit, you need to run the following command once in the terminal. Note that the chmod variant of macOS does not understand the +x short notation. You must explicitly specify for whom the execute bit is set. a means “for all.”

 

$ chmod +x my-script-file.sh (Linux, Git Bash)

$ chmod a+x my-script-file.sh (macOS)

 

Thus, chmod is used to change the access bits. The command supports quite diverse syntax variants. (You can run man chmod if you’re interested in the details). For us, only the chmod +x variant is relevant, for setting the execute bit.

 

Writing Your First Bash Script

To write your first script, you want to launch your favorite editor: Visual Studio Code (VS Code) is a good choice, but any editor is fine, including the minimalist nano program that you can run directly in a terminal. Then, enter the following two lines:

 

#!/bin/bash

echo "Hello, World!"

 

Save this file as hello-world.sh. By using chmod, make the script executable, with the following command:

 

$ chmod +x hello-world.sh      (Linux, Git Bash)

$ chmod a+x hello-world.sh  (macOS)

 

To check if a script works, you need to type its name and press (Enter). Note that you must prefix the name with ./. In this way, you tell Bash to look for your script in the current directory. (. is a short notation for the currently active directory.)

 

$ ./hello-world.sh

   Hello, World!

 

If Script Execution Does Not Work

If the execution of a script triggers an error, various reasons may be the cause:

  • Did you specify the filename correctly?
  • Did you use chmod +x?
  • Did you specify the hash bang line correctly?
  • Is Bash not installed on your computer at all? If you want to run the script using Zsh, you need to change the hash bang to #!/bin/zsh or #!/bin/sh.

Elementary Syntax Rules

Besides the hash bang, you must follow a few other rules in your scripts:

  • Lines beginning with # are comments and are not evaluated.
  • You can use the \ character for long commands that run across multiple lines. The \ character must be placed exactly at the end of the line, and no more spaces may follow!
  • Code indentation is allowed but is optional.
  • Bash is extremely picky about the use of spaces in code. s="abc" is correct; the variants s ="abc" or s= "abc" as well as s = "abc" are all wrong and will trigger errors!

Conversely, in some cases, spaces are required before or after parentheses for Bash to recognize the construct correctly. If your script does not work as expected and the error messages are vague or misleading, you should first check the use of spaces!

 

A “Real” Example: Backup Script

Of course, you’re not reading this post to run “Hello World!” scripts. This second example is a bit longer, but it performs a real task: It creates a compressed backup of all files in your documents directory and stores the backup in the mybackups directory:

 

#!/bin/bash

# Sample file backup-documents.sh

# Save the contents of this directory

documentdir=~/documents

# Backup location

backupdir=~/mybackups

# Create backup directory

mkdir -p $backupdir

# returns e.g., date=27 for 2023-03-27

day=$(date '+%d')

# Create backup, save as documents-<day>.tar.gz

echo "Backup file: $backupdir/documents-$day.tar.gz"

tar czf $backupdir/documents-$day.tar.gz -C $documentdir .

 

How the Backup Script Works

This script requires some explanation:

  • documentdir=~/documents stores the name of your documents directory in the documentdir variable. ~ is a short notation for your home directory (e.g., /home/kofler on Linux, but /Users/kofler on macOS). Depending on the operating system or distribution you use, you’ll need to use the English word Documents. When macOS displays Documents in the Finder with a German language setting, the real directory name is actually Documents!
  • backupdir=~/mybackups stores the desired backup location in another variable.
  • mkdir -p $backupdir creates the backup directory. The -p option prevents error messages if the directory already exists (which is the case after the second execution of the script at the latest).
  • day=$(date '+%d') stores the day of the month in the day variable. date is a command which normally returns the whole date including time. Using the additional parameter +%d you supply only the day of the month (01 to 31) instead. The notation $(...) means: Execute the command contained in the parentheses and return the result.
  • The echo command outputs the exact path of the new backup file on the screen. The output is just feedback for you to understand that the script works as intended. Note that you must specify variable names without $ when assigning them but prefix them with $ when reading them. This is one of the many strange syntax rules of Bash.
  • Finally, the tar command creates a backup of all files in the $documentdir directory. The syntax of this command is also weird:
    • czf specifies what the command is supposed to do (create, zip, file). The next parameter specifies the location of the backup file, for example, /home/kofler/ documents-27.tar.gz.
    • -C determines which directory is to be active during the execution of tar. In this example, it should not be the currently active directory, but the directory where the files to be backed up are located.
    • A . at the end of the command means that the entire contents of the directory must be backed up (and not just selected files, which would also be conceivable).

 

What This Script Gives You

If you run this script once a day, after some time, you’ll have 31 backup versions in the mybackups directory, from documents-01.tar.gz to documents-31.tar.gz. While this script is a bit wasteful, it does give you the ability to recover any documents that were mistakenly overwritten or deleted within the last month.

 

For starters, this example might have been bit much all at once. But don’t worry, a lot of basic information follows in the course of this chapter, which makes this code easier to understand. My primary goal in this example was simply to show you what “real” scripts look like.

 

Sample Files: This script, along with many other longer listings, can be found in the Product Supplements link here.

 

Running Scripts without ./

Do you find it annoying that you always must prepend ./ when running scripts (or even the full path if you’re currently in a different directory)? There is a solution to this!

  • Custom script directory: First, you need to set up a separate directory for your scripts, for example, myscripts in your home directory.
  • Extend the PATH variable: Second, you want to add the full path for this directory to the PATH environment variable. By default, PATH contains various directories in which Bash searches for commands. So, when you run ls, Bash searches all the directories contained in PATH in sequence. If PATH also contains myscripts, then this directory will also be searched. To change PATH, you can use an editor to open the .bashrc file in your home directory (or .zshrc if you use Zsh). At the end of this file, you must add the following line:

# at the end of .bashrc or .zshrc

...

export PATH=/home/kofler/myscripts:$PATH

 

Instead of /home/kofler/myscripts, you must of course specify the full path to your own script directory. Also pay attention to the correct syntax! No spaces must exist before and after = and :. Note also that PATH is written without the $ character the first time, but with the $ character the second time.

 

For this change to take effect, you must log out and log back in. From now on, you can run scripts stored in this directory simply by naming them.

 

Editor’s note: This post has been adapted from a section of the book Scripting: Automation with Bash, PowerShell, and Python by Michael Kofler. Dr. Kofler is a programmer and Linux administrator. He studied electrical engineering/telematics at Graz University of Technology. He has been one of the most successful and versatile computing authors in the German-speaking world for many years. His current topics include Linux, Docker, Git, hacking and security, Raspberry Pi, and the programming languages Swift, JavaPython, and Kotlin. Dr. Kofler also teaches at the Joanneum University of Applied Sciences in Kapfenberg, Austria.

 

This post was originally published in 5/2025.