Introduction to Shell Programming in Linux: A Beginner’s Guide with Examples

Introduction to Shell Programming in Linux_ A Beginner’s Guide with Examples
Shell programming in Linux means writing commands, variables, conditions, loops and functions in a plain text file (a shell script) that the Linux shell runs for you. Start the file with a shebang such as #!/bin/bash, make it executable with chmod +x script.sh, and run it with ./script.sh.

Power up any Linux development board, open a UART console, and the first thing you meet is not a desktop. It is a blinking prompt. That prompt belongs to the shell, and learning to program it is one of the best skills an embedded or IoT engineer can pick up early.

This introduction to shell programming in Linux keeps everything practical. Here is what you will walk away with:

  • What a shell is, and how it differs from a terminal and the kernel
  • What shell scripting is, and how to write and run your first script
  • The six building blocks of every script: variables, input, conditions, loops, functions and exit status
  • Essential shell programming in Linux commands and examples you can run today
  • Honest advantages and disadvantages of shell programming
  • How embedded Linux and IoT engineers use shell scripts on real projects

Let’s start from the ground up.

What Is a Shell in Linux?

A shell is a program that reads the commands you type, asks the Linux kernel to carry them out, and shows you the result. It is the bridge between you and the operating system.

Think of it as a translator standing between two people who do not share a language:

You (keyboard) -> Shell (Bash) -> Kernel -> Hardware (CPU, RAM, storage, UART)

Every time you press Enter, the shell quietly does five jobs:

  • Reads the line you typed (or the next line of a script)
  • Expands wildcards, variables and substitutions such as *.c, $HOME and $(date)
  • Launches the program you asked for and waits for it to finish
  • Connects programs together using pipes and redirection
  • Reports an exit status so a script can decide what to do next

registor_now_P

Shell vs Terminal vs Kernel

These three get mixed up constantly. Here is the clean difference:

  • Kernel: the core of Linux. It manages the CPU, memory, drivers and devices.
  • Shell: the command interpreter that talks to the kernel for you (Bash, Zsh, dash).
  • Terminal: the window or serial console that displays the shell. It is only the screen and keyboard layer.

Popular Linux Shells

ShellWhere you will meet it
BashDefault on most Linux distributions and the best place to start
sh (dash)The lean POSIX shell behind /bin/sh on Debian and Ubuntu
ZshRich interactive features; the default shell on macOS
FishFriendly auto-suggestions and syntax colors for daily use
kshKornShell, whose features influenced many later shells
BusyBox ashTiny shell found on space-constrained embedded Linux images

Check which shell you are running:

echo $SHELL        # your default login shell
echo $0            # the shell running right now
cat /etc/shells    # valid login shells on this system

Good to know: $SHELL shows your default login shell, while $0 shows the shell you are using at this moment. They can differ.

What Is Shell Scripting?

Shell scripting is the practice of saving a series of shell commands in a text file so they run together, in order, without typing each one by hand. That file is a shell script, and it usually ends with .sh.

Shell scripting vs shell programming:

  • In everyday talk, both terms mean the same activity
  • Shell scripting highlights automation: saving a sequence of commands
  • Shell programming highlights logic: variables, decisions, loops and functions that make a script smart

Where shell scripts show up in real work:

  • Backups, builds and deployments
  • Startup tasks on Linux and embedded Linux boards
  • Log parsing and text processing
  • CI/CD pipelines and DevOps tooling
  • Hardware bring-up, testing and production-line automation

How to Do Shell Programming in Linux (Step by Step)

Time to write your first script. You only need a Linux terminal: Ubuntu, Raspberry Pi OS, a virtual machine, or WSL on Windows. Every example in this guide targets Bash.

Step 1: Confirm Bash Is Available

bash --version

Step 2: Create the Script File

nano hello.sh

Step 3: Write Your First Script

#!/bin/bash

# hello.sh - my first shell script
echo "Hello, Embedded World!"
echo "Today is $(date '+%A, %d %B %Y')"

Step 4: Make It Executable

chmod +x hello.sh

Step 5: Run It

./hello.sh

Sample output:

Hello, Embedded World!
Today is Monday, 26 October 2026

What just happened?

  • #!/bin/bash is the shebang. It tells Linux which interpreter should run the file.
  • # starts a comment, which the shell ignores.
  • echo prints text to the screen.
  • $(date ...) is command substitution: the shell runs the command and drops its output into your text.
  • chmod +x adds execute permission to the file.
  • ./ runs the file from the current folder, because that folder is normally not in your PATH.

Pro tip: You can also run bash hello.sh without chmod. When something misbehaves, run bash -x hello.sh to watch every command as it executes.

Shell Programming in Linux Explained: The Six Building Blocks

Let’s explain shell programming in Linux through the six building blocks that every script is made of.

1. Variables

board="Raspberry Pi 4"
units=3
echo "Board: $board, Units: $units"
  • No spaces around =. Writing units = 3 makes the shell hunt for a command named units.
  • Read a value with $, and wrap it in double quotes to protect spaces.
  • Use ${name} when text sits right next to a variable, for example ${board}_log.txt.
  • Use export NAME=value to pass a variable on to child processes.

2. Input and Command-Line Arguments

#!/bin/bash
read -p "Enter your name: " name
echo "Welcome, $name! Arguments passed: $#. First one: $1"

The special variables you will use every day:

VariableMeaning
$0Name of the script
$1, $2, …First, second, … argument
$#Number of arguments
$@All arguments, each kept as a separate word
$?Exit status of the last command (0 means success)
$Process ID of the current shell

3. Conditions (if, elif, else)

#!/bin/bash

if [ -e /dev/ttyUSB0 ]; then
    echo "USB-serial adapter detected."
elif [ -e /dev/ttyACM0 ]; then
    echo "USB CDC device detected (for example, an Arduino)."
else
    echo "No serial device found."
fi

Handy test operators:

  • Files: -e exists, -f regular file, -d directory, -r readable, -w writable, -x executable
  • Numbers: -eq, -ne, -gt, -lt, -ge, -le
  • Strings: =, !=, -z (empty), -n (not empty)
  • Spaces matter: [ -f "$file" ] works, but [-f "$file"] does not

4. Loops (for, while, until)

# for: go through a list
for i in 1 2 3 4 5; do
    echo "Reading sensor $i"
done

# while: repeat as long as the condition is true
count=1
while [ "$count" -le 3 ]; do
    echo "Attempt $count"
    count=$((count + 1))
done
  • until is the mirror image of while: it runs until the condition becomes true
  • break leaves a loop early, and continue skips to the next round
  • $(( ... )) performs integer arithmetic

5. Functions

#!/bin/bash

check_file() {
    local path="$1"
    if [ -f "$path" ]; then
        echo "Found: $path"
        return 0
    else
        echo "Missing: $path"
        return 1
    fi
}

check_file "/etc/hostname"
check_file "/tmp/nothing_here"
echo "Status of the last call: $?"
  • Define a function before you call it
  • local keeps a variable inside the function
  • return gives back a status code from 0 to 255. To hand back data, use echo and capture it with $(...)

6. Exit Status, && and ||

Every command finishes with an exit status: 0 means success, anything else means something went wrong. The shell lets you chain commands using that result:

  • && runs the next command only if the previous one succeeded
  • || runs the next command only if the previous one failed
  • ; runs the next command no matter what
make && echo "Build OK" || echo "Build failed"

Essential Shell Programming in Linux Commands

Scripts are only as strong as the commands inside them. Keep this quick reference handy:

CategoryCommandsWhat they do
Navigation and filespwd, ls, cd, mkdir, cp, mv, rm, touchMove around and manage files and folders
Viewing contentcat, less, head, tail -fRead files; tail -f follows a log live
Search and textgrep, find, sed, awk, cut, sort, uniq, wcSearch, filter, transform and count text
Permissionschmod, chown, ls -lControl who can read, write and execute
Processesps, top, kill, jobs, bg, fgInspect and control running programs
System infouname -a, df -h, du -sh, free -h, dmesgCheck kernel, disk, memory and driver messages
Networkingping, ssh, scp, ip aReach and manage remote boards
Helpman, --help, typeFind out what any command does

Pipes and Redirection: The Real Superpower

Linux programs are small and focused. The shell snaps them together like building blocks:

  • | (pipe) sends the output of one command into the next
  • > writes output to a file (and overwrites it)
  • >> appends output to a file
  • < feeds a file into a command as input
  • 2> redirects error messages
  • 2>&1 sends error messages to the same place as normal output
# Count the CPU cores on this board
grep processor /proc/cpuinfo | wc -l

# Show build output on screen and save it to a log file
make 2>&1 | tee build.log

Shell Programming in Linux Examples You Can Run Today

Reading syntax is useful. Running scripts is where it clicks. These four examples move from beginner level to real embedded work.

Example 1: Timestamped Backup Script

#!/bin/bash

# backup.sh - archive a project folder with a timestamp
SRC_DIR="$HOME/projects"
PROJECT="firmware"
DEST="$HOME/backups"

STAMP=$(date +%Y%m%d_%H%M%S)
mkdir -p "$DEST"

tar -czf "$DEST/${PROJECT}_$STAMP.tar.gz" -C "$SRC_DIR" "$PROJECT"

echo "Backup saved: $DEST/${PROJECT}_$STAMP.tar.gz"
  • Uses variables and command substitution with date
  • ${PROJECT} needs braces, otherwise the shell reads $PROJECT_ as one variable name
  • To run it every night at 2:00 AM, add 0 2 * * * /home/user/backup.sh through crontab -e

Example 2: Check Which IoT Nodes Are Online

#!/bin/bash

# check_nodes.sh - report which IoT nodes answer a ping
NODES=("192.168.1.10" "192.168.1.11" "192.168.1.12")

for node in "${NODES[@]}"; do
    if ping -c 1 -W 2 "$node" > /dev/null 2>&1; then
        echo "[ UP ]   $node"
    else
        echo "[ DOWN ] $node"
    fi
done
  • An array holds several addresses (arrays are a Bash feature)
  • "${NODES[@]}" loops over every element safely
  • The exit status of ping drives the if, and > /dev/null 2>&1 silences its output

Example 3: Log Board Temperature to a CSV File

#!/bin/bash

# temp_logger.sh - log SoC temperature every 5 seconds
ZONE="/sys/class/thermal/thermal_zone0/temp"
LOG="$HOME/templog.csv"

if [ ! -r "$ZONE" ]; then
    echo "Thermal sensor not found on this board." >&2
    exit 1
fi

[ -f "$LOG" ] || echo "timestamp,temp_c" > "$LOG"

while true; do
    raw=$(cat "$ZONE")
    temp=$(awk -v t="$raw" 'BEGIN { printf "%.1f", t / 1000 }')
    echo "$(date '+%F%T'),$temp" >> "$LOG"
    sleep 5
done
  • Reads a hardware value from the /sys virtual filesystem. Linux reports it in millidegrees Celsius, so 45000 means 45.0 °C
  • >&2 sends the message to the error stream, and exit 1 signals failure
  • [ -f "$LOG" ] || echo ... writes the CSV header only when the file does not exist yet
  • while true loops forever, so press Ctrl+C to stop it

Example 4: Build, Check Size and Flash STM32 Firmware

#!/bin/bash

# build_flash.sh - build firmware, show memory usage, flash an STM32 board
set -euo pipefail

ELF="build/firmware.elf"
BIN="build/firmware.bin"

need() { command -v "$1" > /dev/null || { echo "Missing tool: $1" >&2; exit 1; }; }

need make
need arm-none-eabi-size
need st-flash

echo "==> Building..."
make -j"$(nproc)"

echo "==> Memory usage:"
arm-none-eabi-size "$ELF"
echo "==> Flashing..."
st-flash write "$BIN" 0x08000000

echo "Done."
  • set -euo pipefail stops the script on errors, undefined variables and failed pipelines
  • The small need helper checks that every tool exists before real work begins
  • $(nproc) uses all CPU cores for a faster build
  • Adjust the file paths to your project. 0x08000000 is the usual start of flash memory on STM32 devices

Advantages of Shell Programming

  • No compile step: write, save and run. Perfect for quick fixes and experiments.
  • Automates repetitive work: backups, builds, log cleanup and deployments run with one command.
  • Glues tools together: pipes let small programs cooperate without custom code.
  • Available almost everywhere: every Linux system has a shell, and minimal embedded images ship BusyBox ash.
  • Lightweight: no heavy runtime or IDE, which suits resource-constrained boards.
  • Direct system access: files, processes, devices and network tools are one command away.
  • Easy to debug: run line by line or trace with bash -x.
  • Great for DevOps and CI/CD: most build and deployment pipelines lean on shell scripts.

Explore Courses - Learn More

Disadvantages of Shell Programming

  • Slow for heavy computation: it is interpreted and starts a new process for many tasks, so number crunching belongs in C, C++ or Python.
  • Hard to maintain at scale: long scripts get tangled fast, with no strong typing or real modules.
  • Limited data structures: Bash offers arrays and associative arrays, but nothing like structs or objects.
  • Fragile syntax: a missing space or an unquoted variable can change the meaning of a line.
  • Portability gaps: Bash, dash, Zsh and BusyBox ash do not support exactly the same features.
  • Weak default error handling: a script keeps going after a failed command unless you add checks.
  • Security risks: careless quoting, eval and scripts run as root can open the door to command injection.
  • Not for real-time work: shell cannot give the deterministic timing that ISRs and control loops need.

Shell Script vs C vs Python: Which One Should You Use?

TaskBest choiceWhy
Automate build, flash and log captureShellGlues existing tools together in a few lines
Boot-time setup on a minimal Linux imageShell (POSIX sh)Already present, no extra runtime needed
Parse JSON, call APIs, analyze sensor dataPythonRich libraries and readable data handling
Drivers, ISRs and real-time controlC / C++Direct hardware access and predictable timing
One-off file renaming or log filteringShellFaster than writing and compiling a program

Rule of thumb: use shell to orchestrate, C to control hardware, and Python to analyze data.

Shell Programming in Embedded Linux and IoT

Most guides stop at generic admin examples. On an embedded Linux board, the shell does much more:

  • Board bring-up: check what the kernel detected with dmesg (it may need sudo), lsusb, and i2cdetect -y 1 (the bus number varies by board)
  • Startup scripts: embedded images run scripts at boot, such as init scripts under /etc/init.d/ on BusyBox-based systems, to mount filesystems, start the network and launch your application
  • Serial log capture: record UART output while you test firmware (snippet below)
  • Factory and production tests: one script flashes the device, runs self-tests and prints PASS or FAIL
  • Remote maintenance: over SSH, small scripts collect logs, restart services and report device health
  • Edge data pipelines: read a sensor from /sys, filter it with awk, then publish it to an MQTT broker with mosquitto_pub
# Capture 115200-baud UART output into a timestamped log file
stty -F /dev/ttyUSB0 115200 cs8 -parenb -cstopb raw -echo
cat /dev/ttyUSB0 | tee "uart_$(date +%F_%H%M%S).log"

On many distributions you must add your user to the dialout group before you can open serial ports.

Bash vs POSIX sh: Know Your Target Before You Write

A script that runs perfectly on your Ubuntu laptop can fail on a minimal board image. The /bin/sh there may be dash or BusyBox ash, which stay close to the POSIX standard and may not include Bash extensions.

FeatureBashPOSIX sh (the safe choice)
ArraysSupportedNot part of the standard
[[ ... ]] testsSupportedUse [ ... ]
read -p promptSupportedUse printf, then read
source fileSupportedUse . file
== inside [ ]AcceptedUse =

Pro tip: If a script must run on a minimal image, use #!/bin/sh, avoid the Bash-only features above, and check it with sh -n script.sh and the ShellCheck tool. Keep timing-critical logic in C and let the shell orchestrate around it.

Best Practices for Clean and Safe Shell Scripts

  • Start with a shebang: #!/bin/bash, or #!/usr/bin/env bash for portability
  • Quote your variables: "$var" protects against spaces and special characters
  • Add set -euo pipefail to stop on errors, unset variables and failed pipelines (learn its quirks before you rely on it)
  • Use clear names: UPPER_CASE for constants and lower_case for local variables
  • Break repeated logic into functions and use local inside them
  • Validate input and check exit codes instead of assuming success
  • Create temporary files with mktemp and clean them up with trap
  • Prefer shell globs like *.log over parsing the output of ls
  • Run ShellCheck on every script. It catches most beginner bugs instantly
  • Never run a script you do not understand as root, and think twice before piping downloaded code straight into a shell

A reusable starter template:

#!/usr/bin/env bash

set -euo pipefail

TMP=$(mktemp)
cleanup() { rm -f "$TMP"; }
trap cleanup EXIT

echo "Working file: $TMP"

# your logic goes here

Common Beginner Mistakes (and Quick Fixes)

MistakeWhat you seeQuick fix
Spaces around = (x = 5)x: command not foundWrite x=5
Forgetting chmod +xPermission deniedRun chmod +x script.sh
Windows (CRLF) line endingsbad interpreter: /bin/bash^MRun dos2unix script.sh
Unquoted variablesBreaks on names with spacesWrite "$var"
Missing spaces inside [ ][: missing ] or command not foundWrite [ -f "$file" ]
Running Bash code with shSyntax error: “(” unexpectedRun bash script.sh or fix the shebang

Your Shell Programming Roadmap

  1. Get comfortable with the terminal: navigation, files and permissions
  2. Master pipes, redirection and text tools: grep, sed and awk
  3. Write small scripts: variables, conditions and loops
  4. Level up: functions, arguments with $1 and getopts, case statements, trap and error handling
  5. Automate a real workflow: build, flash, test and log, then schedule it with cron or a systemd timer
  6. Pair shell with C/C++ and Python: shell orchestrates, C controls hardware, Python analyzes data

Key Takeaways

  • A shell is a command interpreter between you and the Linux kernel, and Bash is the best one to start with
  • Shell scripting saves commands in a .sh file, and shell programming adds variables, conditions, loops and functions
  • Every script needs a shebang, execute permission (chmod +x) and a run command (./script.sh)
  • Quote your variables, check exit codes and run ShellCheck
  • Shell is excellent for automation and glue work, and a poor fit for heavy computation or real-time control
  • In embedded Linux, shell scripts power bring-up, startup, logging, testing and field maintenance

Talk to Academic Advisor

Conclusion

This introduction to shell programming in Linux covered what a shell is, what shell scripting is, the six building blocks, essential commands, practical examples, and the real advantages and disadvantages of shell programming. Your best next step is simple: pick one repetitive task from your own workflow, automate it with a script, and improve it a little every week.

If you want to turn these skills into a career, the Indian Institute of Embedded Systems (IIES) in Bangalore trains engineers in embedded systems, IoT an

FAQs

Shell programming in Linux is writing scripts made of shell commands, variables, conditions, loops and functions that the Linux shell (usually Bash) executes to automate tasks. It turns repetitive terminal work into one reusable command.

The shell is the program that interprets your commands. The terminal is only the window or serial console that displays the shell. You can run different shells inside the same terminal.

Bash. It is the default on most Linux distributions, has the largest amount of documentation and community help, and the skills carry over to other shells.

Yes. Embedded Linux work involves board bring-up, build automation, log capture, startup scripts and remote maintenance, and shell scripts handle all of these with very little overhead. Real-time and hardware-level code still belongs in C or C++.

Yes. Install WSL (Windows Subsystem for Linux), use Git Bash, or run a Linux virtual machine. A Raspberry Pi also makes an excellent low-cost practice machine.

Author

Embedded Systems and IOT Trainer– IIES

Updated On: 26-10-26


10+ years of hands-on experience delivering practical training in Embedded Systems and it's design