Table of Contents
Getting Started with Shell Scripting
Shell scripting lets you automate sequences of commands that you would otherwise type manually in the terminal. In this part of the course you focus on what makes shell scripts unique, how they are executed, and how they fit into everyday Linux usage, without going deep into details that belong to later subchapters such as variables, conditionals, or loops.
What a Shell Script Is
A shell script is simply a text file that contains a list of commands that the shell can interpret and execute in order. You already know how to type commands in an interactive shell session. A script is that same interaction captured in a file so that it can be repeated, shared, and modified.
The key idea is that everything you already learned about the command line still applies. Each nonempty line in a shell script is usually one command or a pipeline of commands. The shell runs them one after another, from top to bottom, just like you would by hand.
The most common scripting shell on Linux is bash, but other shells such as sh, zsh, and fish also support scripting. In this chapter the word “shell script” usually means “a script run by a POSIX style shell such as bash or sh.”
A shell script is text that is interpreted at run time by a shell program such as bash. It is not compiled into a binary.
This means scripts are easy to read and edit, but you must be careful because the shell will try to execute what you write exactly as written.
Why Use Shell Scripts
Shell scripting is most valuable when you want to automate tasks that involve running commands, working with files, or gluing together existing tools.
Common uses include:
Automating daily or repetitive tasks, for example cleaning up logs, syncing directories, or converting a batch of files.
Setting up an environment, for example exporting some environment variables and starting several programs.
Running sequences of commands that need to be executed in a specific order, such as backing up data then rotating old backups.
Glues between programs, for example running one command, capturing its output, then passing that output into another command.
Shell scripts are especially powerful because they can call any program installed on your system, not just built in shell features. This is different from many other languages where you first write code, then separately worry about how to invoke system tools. With shell scripts system tools are the main building blocks.
Basic Structure of a Shell Script
A typical shell script has a simple structure. It starts with an interpreter line, sometimes called a shebang, followed by comments and commands.
The interpreter line is usually the first line in the file and tells the kernel which program should interpret the script.
The most common form for bash scripts is:
#!/usr/bin/env bash
This line means: use the env program to locate the bash interpreter in the current environment and use that interpreter to run the rest of the file. This pattern is portable across different systems where bash might live in slightly different paths.
For more minimal scripts that try to be compatible with many Unix systems you often see:
#!/bin/sh
That line asks the system to run the script with whatever program provides sh. On modern Linux this is usually a shell that supports a standard POSIX subset, which is often enough for simple scripts.
After the shebang, you can write comments starting with #. The shell ignores the rest of any line that begins with # outside of special contexts. Comments are essential in scripts because they help both you and others understand what the script is doing and why.
#!/usr/bin/env bash
# This script prints a greeting.
echo "Hello from a shell script"
In this example there is one command, echo, which prints text. The structure is simple: interpreter line, comments, then commands executed one after another.
How the Shell Executes a Script
When you run a shell script, the shell reads the file and executes it according to the interpreter specified by the shebang. It processes the file line by line. Each line is parsed into a command and arguments, variables are expanded, quoting is interpreted, and so on. Details of variables and other features are covered in later sections, so here you focus on the basic execution flow.
If a script does not provide a shebang, how it is executed can depend on how you invoke it. For example, you can explicitly run bash and give the script file name:
bash myscript.sh
In this case bash itself reads myscript.sh and executes it, even if there is no interpreter line in the script.
If you try to execute the script directly as a program, for example by running ./myscript.sh, then the kernel relies on the first line to know which interpreter should process the file. If there is no valid shebang and the file is marked executable, you may see an error or unexpected behavior.
The shell also uses an exit status for each command and for the script overall. This exit status is an integer where 0 usually means success and any nonzero value means some sort of failure. You will use exit statuses more explicitly later when you learn about conditionals, but it is useful to know that every script produces one.
Naming and Saving Shell Scripts
Since shell scripts are text files you create them with any text editor. The traditional extension is .sh but it is not required. Linux decides whether a file is executable by its permission bits and, when executed, by its shebang line, not by its name.
Even so, it is helpful to use consistent naming conventions. Many people use descriptive names like backup_logs.sh or deploy_site. The actual content and executable permission matter more than the file name suffix.
When saving scripts be careful about:
Using plain text. Do not use word processors that insert formatting or special characters.
Line endings. On Linux you want Unix style line endings, which are single \n characters, not Windows style \r\n. Most Linux editors handle this correctly by default.
Character encoding. Use UTF 8 unless you have a special reason to do otherwise. This is the standard on modern Linux systems and avoids many subtle issues.
Running Shell Scripts
There are two main ways to run a shell script: by passing it as an argument to the shell, or by executing it directly as a program. Both methods are useful in different situations.
The first method uses the shell explicitly:
bash script.sh
In this case bash reads script.sh as input, and you do not need any particular permissions on script.sh beyond read permission. You can also use sh or other shells:
sh script.shThe second method uses executable permission and the shebang line. First you make the script executable by setting the execute bit. You will learn the details of changing permissions in another chapter, so here you only need to know that a common way is:
chmod +x script.shOnce the script is executable you can run it like a program:
./script.sh
The ./ prefix tells the shell to execute the file script.sh located in the current directory. Whether this succeeds depends on the shebang line. If the first line is #!/usr/bin/env bash then the kernel launches bash and hands it this file. If there is no shebang you may get a “Exec format error” or some shells may try to execute it in other ways, which is not reliable.
To run a script directly as ./scriptname, the file must be readable, must have the execute permission set, and must contain a valid shebang line.
If any of these conditions are not met, you can still usually run the script by calling bash scriptname, but that relies on you explicitly choosing the interpreter on the command line.
Scripts vs Interactive Commands
At first, you might write scripts that are nothing more than a list of commands you have already run by hand. That is a good start and it is exactly how many administrators and developers begin to automate tasks.
When you copy commands from your terminal history into a script, consider a few things that differ between interactive and scripted use.
Interactive commands often rely on your current directory. In a script you may want to use absolute paths or explicitly change directory with cd at the top.
Interactive commands may depend on environment variables that your login shell sets. When a script runs it inherits environment variables from the parent process but might not get interactive only settings such as colored prompts. If a script depends on a specific variable, you should set it in the script or ensure your environment always exports it.
Interactive commands may pause for input. Scripts are better when they can run without manual interaction. Even for simple scripts, you should think about whether the commands you are using might block waiting for user input.
As you learn more about shell scripting you will replace manual input with variables and arguments, and you will add control flow features so that scripts can make decisions without human intervention.
The Role of `#!` (Shebang) in Detail
The two characters #! at the start of a script followed by an interpreter path are a special sequence recognized by the kernel. This is called the shebang. It is not a shell feature, it is handled directly by the kernel when executing a file.
When you run ./myscript, the kernel inspects the start of the file. If the first two bytes are #!, then the rest of that first line is treated as the interpreter path and optional arguments. Conceptually, the kernel behaves as if you had run:
/path/to/interpreter [optional-args] path/to/myscript [original-arguments]So for a script whose first line is:
#!/usr/bin/env bashand that you invoke as:
./myscript hello worldthe kernel effectively turns this into:
/usr/bin/env bash ./myscript hello world
This explains why the shebang is so important. It controls which shell or interpreter language actually runs your script, and it means you can write scripts for other languages such as python or perl using the same mechanism.
Comments and Documentation in Scripts
Since scripts are interpreted text, it is easy to forget what a script does or why you wrote it. Comments are your main tool for documentation. Any line beginning with # that is not the first line shebang is a comment.
You can use comments at the top of the file to document the purpose of the script, how to run it, and any prerequisites. You can also insert comments before sections of commands to explain a nonobvious sequence.
For example:
#!/usr/bin/env bash
# Backup the /etc directory to a timestamped archive in ~/backups.
# Usage: ./backup_etc.sh
BACKUP_DIR="$HOME/backups"
TIMESTAMP="$(date +%Y%m%d-%H%M%S)"
mkdir -p "$BACKUP_DIR"
# Create a compressed tar archive of /etc with the timestamp in the name.
tar czf "$BACKUP_DIR/etc-$TIMESTAMP.tar.gz" /etc
Later chapters will cover variables and command substitution in detail, which appear here in BACKUP_DIR and TIMESTAMP. For now focus on the role of structure and comments. The script has a clear description and clear section comments so a future reader can understand the intention quickly.
Shell Scripts as Tools
As you write more scripts, some of them will become part of your personal toolbox. Instead of typing long sequences of commands you can call a single script by name.
To make this comfortable you can place scripts in a directory that is listed in your PATH environment variable. When a directory is in PATH the shell will look there to find executables when you type a command name. Users commonly create a ~/bin directory in their home and add it to PATH in their shell configuration, then place personal scripts there.
Once a script is in a directory on PATH and is executable you can run it from anywhere by typing its name, just like built in commands. This is how many system wide commands are implemented. Many of them are actually shell scripts or other interpreted scripts installed in system directories.
The division between “a script” and “a real program” is mostly a matter of complexity and implementation language. From the user perspective they are both commands.
Shell Script Portability
Not all shells interpret scripts exactly the same way. Although many scripts are written for bash, it is often useful to limit yourself to features that are standard across POSIX shells so that your scripts run on a wider range of systems.
If you choose #!/bin/sh as your shebang you are implicitly promising not to use bash specific features. On many Linux distributions /bin/sh is a lightweight shell such as dash that enforces this more strictly. On others it might be bash in a special compatibility mode.
Portability matters most when you share scripts across different distributions or Unix like systems. If you know your script will only ever run on your own machine you might rely freely on bash specific behavior. The important point is that the choice of interpreter in the shebang controls what language features are available.
Shell Scripting vs Other Languages
Shell scripting is powerful for orchestrating commands, but it is not ideal for every task. It shines when:
You are manipulating files and processes.
You are gluing together command line tools.
You need quick automation without setting up a more complex development environment.
Other languages, such as Python or C, are often more suitable when:
You need complex data structures.
You are building large applications.
Performance and strict error handling are critical.
In practice many Linux users mix shell scripts with other languages. A shell script might prepare the environment, then call a Python script to do heavy processing, then call tar and rsync to archive and sync the results.
Understanding where shell scripting fits within this landscape helps you choose the right tool for each job. In this course, the focus of shell scripting is on using the shell to automate and control the tools that Linux already provides.
Looking Ahead
In the remaining subchapters you will go beyond the basic structure of a script and learn about:
Writing your first complete script from scratch, with interpreter line, comments, and commands.
Using variables to store and reuse values.
Adding conditionals so scripts make decisions based on exit statuses and other tests.
Using loops to repeat actions over sets of files or other lists.
Defining functions to organize and reuse sequences of commands inside a script.
Making scripts executable and integrating them into your environment so they behave like standard commands.
With these building blocks you will be able to turn manual command sequences into reusable tools that save time and reduce errors in your daily Linux work.