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.
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
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.
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.
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 the execution of a script triggers an error, various reasons may be the cause:
Besides the hash bang, you must follow a few other rules in your scripts:
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!
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 .
This script requires some explanation:
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.
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!
# 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, Java, Python, and Kotlin. Dr. Kofler also teaches at the Joanneum University of Applied Sciences in Kapfenberg, Austria.
This post was originally published in 5/2025.