Chapter 19: Shell Scripting Basics: Introduction and First Script
Chapter Objectives
By the end of this chapter, you will be able to:
- Understand the fundamental role of the shell and shell scripting in embedded Linux systems.
- Explain the purpose and syntax of the shebang (
#!
) for script interpretation. - Create, write, and save a basic shell script using a command-line text editor.
- Interpret and modify file permissions to make a shell script executable.
- Execute a shell script and verify its output on a Raspberry Pi 5.
- Debug common initial errors related to script paths, permissions, and syntax.
Introduction
Welcome to the world of automation and control within your embedded Linux system. In previous chapters, you have become proficient at navigating the Linux command line, manipulating files, and interacting with system processes on your Raspberry Pi 5. Each command you typed was a direct instruction, executed immediately. But what if you need to execute a sequence of dozens, or even hundreds, of commands repeatedly? What if you need to perform a complex system check at boot, manage a network connection automatically, or read a sensor and log its data to a file? Performing these tasks manually is not just tedious; it is impractical and prone to error, especially in a deployed embedded device that must operate autonomously.
This is where shell scripting becomes an indispensable skill. A shell script is simply a text file containing a series of commands that the command-line interpreter, or shell, executes sequentially. It is the foundational tool for automating tasks, managing system configurations, and creating custom logic without compiling complex programs. In the context of embedded systems, shell scripts are the glue that holds many components together. They are used for everything from initializing hardware and starting applications upon boot-up to performing routine maintenance and managing application deployments. This chapter will guide you through creating your very first shell script, demystifying the essential concepts of the shebang and file permissions that bring your scripts to life. By the end, you will have transformed a simple text file into an executable program, laying the groundwork for more advanced automation in the chapters to come.
Technical Background
The Shell: Your System’s Conversation Partner
At the heart of your interaction with a Linux system is the shell. It is a powerful program that acts as a command-line interpreter, providing a crucial interface between you and the operating system’s kernel. When you type a command like ls -l
and press Enter, it is the shell that reads your input, parses it to understand that you want to run the ls
program with the -l
option, and then asks the kernel to execute it. The kernel performs the task, and the shell displays the output for you. Think of the shell as a translator, fluently speaking both human-readable commands and the low-level language the kernel understands.
While many shells exist (like csh
, ksh
, zsh
), the most common and de facto standard in the Linux world is the Bourne Again Shell, or Bash. Its prevalence is due to its powerful features, scripting capabilities, and backward compatibility with the original Bourne shell (sh
). On your Raspberry Pi OS and most other embedded Linux distributions, Bash is the default interactive shell, the one you are greeted with when you open a terminal. It is this shell that we will command with our scripts.

A shell script leverages this interactive power by placing a sequence of these commands into a single file. Instead of typing them one by one, you instruct the shell to read the file and execute the commands in order, as if you were typing them yourself. This simple yet profound capability allows for the creation of complex, automated workflows. The script can make decisions, loop through tasks, manipulate text, and interact with other programs, all orchestrated by the logic you define.
The Shebang (#!
): The Script’s Magic Interpreter Directive
When you execute a program, the Linux kernel needs to know how to run it. For a compiled C program, it knows to load the binary code into memory and execute it directly. But a shell script is just a text file. How does the kernel know it’s not a poem or a grocery list? How does it know which interpreter—bash
, python
, perl
—is meant to read and execute the commands within?
This is the critical role of the shebang. The shebang is a special character sequence, #!
, that must appear as the very first two characters of the very first line in a script. It is formally known as the “interpreter directive.” Following the shebang is the absolute path to the interpreter program that should be used to execute the script.
For a Bash script, the shebang line is almost always:
#!/bin/bash
Let’s break this down. The #!
tells the kernel’s program loader, “Do not execute this file directly. Instead, use the program specified on the rest of this line as the interpreter.” The path /bin/bash
is the absolute location of the Bash executable on most standard Linux systems. When the kernel sees this line, it effectively runs the command /bin/bash your_script_name.sh
, passing the script itself as an argument to the Bash interpreter.
%%{init: {'theme': 'base', 'themeVariables': { 'fontFamily': 'Open Sans'}}}%% graph TD subgraph Kernel Execution Flow A[User executes script e.g., ./my_script.sh] --> B{Kernel Opens File}; B --> C{First line starts with #! ?}; C -- Yes --> D["Kernel reads interpreter path<br>e.g., /bin/bash"]; D --> E[Kernel executes command:<br><i>/bin/bash ./my_script.sh</i>]; E --> F[Script runs successfully]; C -- No --> G[Kernel attempts direct execution]; G --> H["Execution Fails for Text File<br><i>Error: cannot execute binary file</i>"]; end %% Styling classDef primary fill:#1e3a8a,stroke:#1e3a8a,stroke-width:2px,color:#ffffff; classDef decision fill:#f59e0b,stroke:#f59e0b,stroke-width:1px,color:#ffffff; classDef process fill:#0d9488,stroke:#0d9488,stroke-width:1px,color:#ffffff; classDef success fill:#10b981,stroke:#10b981,stroke-width:2px,color:#ffffff; classDef error fill:#ef4444,stroke:#ef4444,stroke-width:1px,color:#ffffff; class A,D,E primary; class C decision; class B,G process; class F success; class H error;
What if you omit the shebang? If you run the script from within an already running Bash shell (e.g., bash my_script.sh
), it will work because you are explicitly telling Bash to run it. However, if you try to execute it directly as a standalone program (./my_script.sh
), the system will not know what to do with it and will likely fail with an error. The shebang makes a script self-contained and portable, ensuring it is always executed by the correct interpreter, regardless of the user’s current shell.
Tip: While
#!/bin/sh
is also common and often points to Bash (or a compatible shell like Dash) for POSIX compliance, explicitly using#!/bin/bash
is good practice if your script uses Bash-specific features.
File Permissions: Granting the Right to Execute
In the Linux security model, every file and directory has a set of permissions that control who can read, write, or execute it. This is a fundamental concept that prevents unauthorized access and accidental modification of system files. For a shell script to be run as a program, it must be granted “execute” permission.
Permissions are managed for three classes of users:
- User (u): The owner of the file.
- Group (g): The group the file belongs to.
- Others (o): Everyone else.
For each class, there are three primary permissions:
- Read (r): The ability to view the contents of the file.
- Write (w): The ability to modify the contents of the file.
- Execute (x): The ability to run the file as a program.
When you create a new text file, it typically has read and write permissions for the owner (rw-
) but no execute permission. You can view a file’s permissions with the ls -l
command. The output will look something like this:
-rw-r--r-- 1 pi pi 45 Jul 8 20:30 my_script.sh
The very first part, -rw-r--r--
, represents the permissions. The leading dash indicates it is a regular file. The next nine characters are three sets of three, corresponding to the permissions for user, group, and others. In this case, the user (pi
) can read and write (rw-
), while the group (pi
) and others can only read (r--
). Crucially, the x
is missing for all classes.
To make our script runnable, we must add the execute permission. This is done using the change mode command, chmod
. The chmod
command can modify permissions in two ways: symbolic mode or octal (numeric) mode. For beginners, symbolic mode is often more intuitive.
To add execute permission for the user (the owner), you would use the command:
chmod u+x my_script.sh
Here, u
stands for user, +
means to add a permission, and x
stands for execute. After running this command, the permissions will look like this:
-rwxr--r-- 1 pi pi 45 Jul 8 20:35 my_script.sh
Notice the x
has appeared in the first block of permissions. Now, the owner of the file has the kernel’s permission to execute it. This is the final piece of the puzzle. With a proper shebang and execute permissions, your text file has been transformed into a true, runnable program.

Practical Examples
Let’s apply this theory by creating and running our first shell script on the Raspberry Pi 5. This example will guide you through every necessary step, from creating the file to seeing your automated message appear on the screen.
Step 1: Connecting to Your Raspberry Pi and Creating a Script File
First, ensure your Raspberry Pi 5 is powered on and connected to your network. Connect to its command line using SSH or by opening a terminal directly on the device.
It’s good practice to create a dedicated directory for your work. Let’s make one called scripts
in your home directory (~
).
# Create a new directory for our scripts
mkdir ~/scripts
# Navigate into the new directory
cd ~/scripts
Now, we will use the nano
text editor to create our script file. nano
is a simple, beginner-friendly command-line editor. We will name our script hello_embedded.sh
.
# Create and open a new file named hello_embedded.sh with nano
nano hello_embedded.sh
Your terminal will now be replaced by the nano
editor interface, which is a blank canvas for your script.
Step 2: Writing the Script Content
This is where we provide the instructions for the shell. The first line must be the shebang, followed by the commands we want to run.
Code Snippet: hello_embedded.sh
Type the following content exactly as shown into the nano
editor.
#!/bin/bash
# ####################################################################
# hello_embedded.sh
#
# A simple introductory script for an embedded Linux course.
# This script prints a welcome message and the current system date.
# ####################################################################
# Print a static welcome message to the terminal.
# The 'echo' command is used to display a line of text.
echo "Hello, Embedded World!"
# Print a blank line for better readability.
echo
# Display the current date and time.
# The 'date' command retrieves system time information.
echo "The current system time is:"
date
Code Explanation:
#!/bin/bash
: This is our crucial shebang. It tells the system to use the Bash interpreter to run this file.# ...
: Lines beginning with a#
are comments. The shell ignores them. They are essential for explaining what your code does to yourself and others. Good commenting is a hallmark of a professional developer.echo "..."
: Theecho
command is one of the most fundamental shell commands. It simply prints the text you give it (the “string”) to the standard output, which is your terminal screen.date
: This is another standard Linux command. When executed, it prints the current system date and time. Our script is simply automating the execution of this command.
Once you have typed the content, save the file and exit nano
. You can do this by pressing Ctrl+X
, then Y
to confirm you want to save, and finally Enter
to accept the filename.
Step 3: Verifying File Creation and Permissions
You should now be back at the command prompt. Let’s verify that the file was created and check its initial permissions.
# List the contents of the current directory in long format
ls -l
Expected Output:
-rw-r--r-- 1 pi pi 295 Jul 8 20:45 hello_embedded.sh
As predicted in the technical background section, the file exists, but it lacks the execute (x
) permission. If you were to try running it now, you would get a “Permission denied” error.
# Attempt to run the script (this will fail)
./hello_embedded.sh
Expected Error:
-bash: ./hello_embedded.sh: Permission denied
The ./
at the beginning is important. It tells the shell to look for the script in the current directory. Without it, the shell would only search in the standard system paths, and it would not find your script.
Step 4: Making the Script Executable and Running It
Now, let’s grant the necessary permission using chmod
. We will add execute permission for the user (owner) of the file.
%%{init: {'theme': 'base', 'themeVariables': { 'fontFamily': 'Open Sans'}}}%% sequenceDiagram actor User participant Shell participant Kernel participant ScriptFile as "hello_embedded.sh" User->>Shell: Executes command: ./hello_embedded.sh Shell->>Kernel: Requests execution of "./hello_embedded.sh" Kernel->>ScriptFile: Opens file ScriptFile-->>Kernel: Returns content alt Shebang #!/bin/bash is present Kernel->>Kernel: Reads shebang, identifies /bin/bash as interpreter Kernel->>Shell: Spawns new Bash process, passing script as argument Shell->>ScriptFile: Reads commands sequentially ScriptFile-->>Shell: Returns "echo 'Hello...'" Shell->>Shell: Executes echo command Shell-->>User: Displays "Hello, Embedded World!" Shell->>ScriptFile: Reads next command ScriptFile-->>Shell: Returns "date" Shell->>Shell: Executes date command Shell-->>User: Displays current date/time Shell-->>Kernel: Script execution complete Kernel-->>Shell: Returns exit status Shell-->>User: Command complete else No Shebang Kernel->>Kernel: Tries to execute file directly Kernel-->>Shell: Returns "Permission Denied" or "Exec format error" Shell-->>User: Displays error message end
# Add execute permission for the user
chmod u+x hello_embedded.sh
Let’s check the permissions again to see the change.
ls -l
Expected Output:
-rwxr--r-- 1 pi pi 295 Jul 8 20:50 hello_embedded.sh
The x
is now present in the user permissions block (rwx
). Our script is ready. It’s time to execute it.
# Execute the script
./hello_embedded.sh
Expected Output:
Hello, Embedded World!
The current system time is:
Tue Jul 8 20:51:00 UTC 2025
Success! The Bash interpreter read your script file, executed each command in sequence, and printed the output to your terminal. You have successfully written and run your first shell script.
Common Mistakes & Troubleshooting
Even with a simple script, several things can go wrong. Understanding these common pitfalls is the first step toward becoming a proficient debugger.
Exercises
These exercises will help you solidify your understanding of the concepts covered in this chapter.
- Exercise 1: Modify the Welcome Message
- Objective: Practice editing an existing script.
- Task: Open the
hello_embedded.sh
script innano
. Change the “Hello, Embedded World!” message to “Greetings from Raspberry Pi 5!”. Add anotherecho
line that prints your name. Save the file and run it to verify your changes.
- Exercise 2: Report System Information
- Objective: Add more system commands to a script.
- Task: Create a new script named
system_info.sh
. This script should print the following:- A title, like “System Information Report”.
- The system’s hostname (hint: use the
hostname
command). - The kernel version (hint: use
uname -r
). - The amount of free memory (hint: use
free -h
).
- Remember to add the correct shebang and make the script executable. Run it to see the report.
- Exercise 3: Experiment with Permissions
- Objective: Understand the effect of file permissions.
- Task: Take your working
system_info.sh
script. Use thechmod
command to remove the execute permission (e.g.,chmod u-x system_info.sh
). Try to run it with./system_info.sh
. Observe the “Permission denied” error. Now, add the execute permission back. This time, try to run the script by explicitly invoking the interpreter:bash system_info.sh
. Notice that this works even if the execute bit is not set. Why do you think that is? (Answer: Because you are telling thebash
program to read the file, not asking the kernel to execute the file directly).
- Exercise 4: Create a Script in the System Path
- Objective: Understand how the
PATH
variable works. - Task: There is a directory on your system,
/usr/local/bin
, which is typically in the system’sPATH
and is the standard place for locally installed scripts. Copy yourhello_embedded.sh
script to this directory usingsudo cp hello_embedded.sh /usr/local/bin/hello
. Note that you needsudo
because it’s a system directory. Now, from any directory (e.g., your home directory~
), try running the script by just typing its new name:hello
. It should run without the./
. This demonstrates the power of placing scripts in a directory included in thePATH
.
- Objective: Understand how the
Summary
- A shell is a command-line interpreter that acts as an interface to the operating system kernel. The Bourne Again Shell (Bash) is the standard on most Linux systems.
- A shell script is a text file containing a sequence of commands that the shell can execute, enabling automation of complex tasks.
- The shebang (
#!
) is a directive on the first line of a script that specifies the absolute path to the interpreter (e.g.,#!/bin/bash
) that should be used to run it. - Linux files have permissions for read (
r
), write (w
), and execute (x
) that apply to the user, group, and others. - To run a script as a standalone program, it must have the execute permission set. This is typically done with the
chmod u+x <filename>
command. - Scripts located in the current directory must be executed with a leading
./
(e.g.,./my_script.sh
) to provide an explicit path to the shell.
Further Reading
- Bash Guide for Beginners – A classic, thorough guide by Machtelt Garrels. An excellent starting point for structured learning. Available at The Linux Documentation Project (tldp.org).
- Advanced Bash-Scripting Guide – An in-depth, comprehensive exploration of Bash scripting’s more advanced features. Also from The Linux Documentation Project. tldp.org/LDP/abs/html/.
- GNU Bash Manual – The official reference documentation from the creators of Bash. It is the definitive source for all features and behaviors. www.gnu.org/software/bash/manual/.
- Raspberry Pi Documentation – The command-line – Official documentation from the Raspberry Pi Foundation on using the command line. www.raspberrypi.com/documentation/computers/using_linux.html#the-command-line.
- “The Linux Command Line: A Complete Introduction” by William Shotts – A highly-regarded book that provides a comprehensive and accessible introduction to the Linux command line and shell scripting.
- explainshell.com – An interactive web tool where you can paste a shell command and see a visual breakdown of what each part does.