Before writing a single line of code it is worth understanding what exactly that black window full of text is — the one where so many systems, development and data professionals spend a good part of their working day. Bash is not "the black screen": it is a specific program, with its own history, its own syntax and a well-defined place inside the operating system. In this lesson you will discover what a shell is, why Bash became the de facto standard of the Unix and Linux world, and what its two faces are: the interactive interpreter you converse with, and the programming language you automate with. You will also meet Veloz Envíos, the fictional company that will accompany us throughout the course and whose real problems we will solve lesson by lesson.

Contents

  1. What a shell is and what it actually does
  2. Bash within the Unix shell family
  3. A short history: from Thompson to Bourne Again
  4. Why Bash is still the de facto standard
  5. The two uses of Bash: interactive interpreter and scripting language
  6. Comparison: Bash versus other shells
  7. Where you will run into Bash professionally
  8. Our case study: Veloz Envíos and the veloz-ops toolkit
  9. Your first commands

  1. What a shell is and what it actually does

A shell (literally, an outer casing) is a program that acts as an intermediary between you and the core of the operating system. Its job boils down to a very simple cycle that it repeats over and over:

  1. It displays a prompt (an invitation to type).
  2. It reads the line you type.
  3. It interprets it: it decides which command you want to run and with which data.
  4. It runs that command and waits for it to finish.
  5. It shows the result and goes back to step 1.

The name "shell" is very descriptive: it wraps the kernel (the core of Linux, which manages memory, processes, disks and network) and offers you a human way of asking it for things. You do not talk to the kernel directly; you talk to the shell, and the shell translates.

graph LR
    U[User] -->|types commands| S["Shell: Bash"]
    S -->|system calls| K["Linux kernel"]
    K -->|manages| H["Hardware: CPU, disk, network"]
    K -->|result| S
    S -->|text on screen| U

It is worth separating three concepts that beginners tend to mix up:

Concept What it is Example
Terminal The window or device where you see text and type GNOME Terminal, Windows Terminal, iTerm2
Shell The program that interprets what you type Bash, Zsh, Fish, dash
Kernel The core of the operating system Linux, XNU (macOS)

In other words: you open a terminal, which starts a shell (usually Bash), which in turn requests services from the kernel. When someone says "open a terminal and run this command", what they are really asking is that you hand it to Bash.

  1. Bash within the Unix shell family

Bash stands for Bourne Again SHell, a pun: it is "the Bourne shell again" and at the same time it sounds like born again. That name gives away its lineage. Unix has two big shell families:

  • The Bourne family (sh): the original Unix shell written by Stephen Bourne. From it descend ksh, bash, dash and zsh. It is the family whose syntax was standardized in POSIX and the one used for serious scripting.
  • The C shell family (csh, tcsh): syntax inspired by the C language. Comfortable in its day for interactive use, but discouraged for scripts for decades now.

Bash belongs to the first family and is backwards compatible with sh: almost any script written for the Bourne shell runs in Bash unchanged. On top of that base, Bash added a huge number of its own extensions, colloquially known among professionals as bashisms (arrays, [[ ... ]], brace expansion, process substitution and so on). Those extensions are powerful, but bear in mind that they are not portable to just any shell; we will come back to this in the POSIX portability lesson (08-07).

  1. A short history: from Thompson to Bourne Again

A minimal timeline helps to understand why things are the way they are:

Year Milestone Why it matters
1971 Thompson shell (sh) in the original Unix Introduces the idea of the shell as a replaceable program
1977 Bourne shell by Stephen Bourne Adds variables, flow control, real scripts
1978 C shell by Bill Joy History and job control for interactive use
1983 KornShell (ksh) by David Korn Combines the best of both; hugely influential
1989 Bash 1.0, written by Brian Fox for GNU A free replacement for the Bourne shell
1996 Bash 2.0, maintained by Chet Ramey Consolidation of the modern extensions
2004 Bash 3.0 The version macOS still ships today for licensing reasons
2009 Bash 4.0 Associative arrays, recursive **, coproc
2019 Bash 5.0 EPOCHSECONDS, performance improvements and better wait
2022+ Bash 5.2 and later Fixes and fine-tuning; the base of today's distros

The key fact: Bash was born as free software from the GNU project. When Linux appeared in 1991 it adopted the GNU toolset, and Bash walked straight in as the default shell of practically every distribution. That historical decision is the reason why today, thirty-five years later, you still need to learn it.

  1. Why Bash is still the de facto standard

There are more modern shells that are, in some respects, more pleasant. Even so, Bash keeps its position for very practical reasons:

  • Ubiquity: it is installed on almost any Linux system, on macOS, in the most common containers and in the public cloud images. If you write a script in Bash, chances are it will run on the target machine with nothing else to install.
  • Stability: a script written in 2005 still works today. In infrastructure, that predictability is worth its weight in gold.
  • Network effect: the vast majority of documentation, tutorials, forum answers and cloud provider examples are written in Bash.
  • It is the glue of the Unix ecosystem: Bash does not try to do everything; its job is to chain specialized programs together (grep, awk, sort, curl) to solve a problem. That composition model remains extraordinarily productive.
  • It sits on the critical path of automation: Dockerfiles, CI/CD pipelines, systemd units, crontabs and installers all end up running shell lines.

Put another way: you can pick Zsh or Fish for your daily comfort, but sooner or later you will have to read and write Bash because it is the lingua franca of servers.

  1. The two uses of Bash: interactive interpreter and scripting language

This distinction is fundamental and shapes the whole course.

5.1 Bash as an interactive interpreter

This is the conversational use: you type a command, press Enter, see the result and decide what comes next. It is exploration, diagnosis, one-off work.

whoami
joan

Here you asked a question ("who am I?") and got an immediate answer. In interactive mode Bash gives you conveniences such as history, tab completion and a customizable prompt. We will cover all of that in lessons 01-02 and 02-06.

5.2 Bash as a scripting language

This is the programmatic use: you save a sequence of commands in a text file and run it as a program. Bash then has everything you expect from a language: variables, conditionals, loops, functions, error codes.

#!/usr/bin/env bash
# Minimal report for the Veloz Envíos server
echo "Server: $(hostname)"
echo "Date:   $(date '+%Y-%m-%d %H:%M')"
echo "User:   $(whoami)"

Do not worry about the syntax yet: you do not know what #!/usr/bin/env bash does or why there is a $( ). All of that is explained from Module 3 onwards. What you should take away is the idea:

Aspect Interactive mode Script mode
Goal Explore, diagnose, do something once Repeat reliably and unattended
Who runs it A person, live Cron, systemd, CI/CD, another person
Error tolerance High: you see it and fix it Low: nobody is watching
Priority Speed of typing Readability and robustness
Typical tools History, aliases, completion Functions, error handling, logs

A good Bash professional moves constantly between both worlds: they try an idea interactively and, once it works, crystallise it into a script so they never have to do it by hand again. That is exactly the journey we will take with Veloz Envíos.

  1. Comparison: Bash versus other shells

Shell Origin Strength Weakness When to choose it
sh Bourne, 1977 (today usually a link to another shell) Maximum portability, minimal POSIX syntax No arrays, no [[ ]], very spartan Scripts that must run on any Unix
bash GNU, 1989 Ubiquitous, powerful, exhaustively documented Slower than compiled shells on intensive tasks System scripts, CI/CD, general use
dash Debian Almquist Shell Very fast startup, lightweight POSIX only: no bashisms /bin/sh on Debian/Ubuntu, boot scripts
zsh 1990 Superior completion and customization Subtle differences from Bash in scripts Daily interactive shell; default on macOS
fish 2005 Friendly, suggestions and colors by default Not POSIX compatible: scripts from the internet do not work Interactive use if you value ergonomics
PowerShell Microsoft, 2006 Works with objects, not text Different ecosystem, verbose, rare on Linux Windows and Azure administration

Two important caveats that cause plenty of trouble in practice:

  • On Ubuntu and Debian, /bin/sh is dash, not Bash. If you write a script with bashisms and start it with #!/bin/sh, it will fail with baffling errors. The rule: if you use Bash features, declare #!/usr/bin/env bash.
  • On macOS, the system /bin/bash is version 3.2 from 2007 for licensing reasons (Apple does not adopt the GPLv3 license). That is why associative arrays, which arrived in Bash 4, do not work there. We will sort this out in lesson 01-02.

  1. Where you will run into Bash professionally

It is not a language people use "for fun": it shows up because it is the path of least resistance in these scenarios.

  • Linux servers: when you log in to a machine over SSH, what greets you is a shell. Diagnosing why a service will not start or why the disk filled up is Bash work.
  • CI/CD: GitHub Actions, GitLab CI and Jenkins run their steps as shell lines. A GitHub Actions run: | is literally Bash.
  • Docker containers: every RUN in a Dockerfile is a shell line, and the entrypoint.sh scripts that prepare a container before starting the application are Bash scripts.
  • Cloud: the user-data of an EC2 instance, the boot scripts of an Azure or GCP VM, and much of the aws/gcloud/az documentation are Bash.
  • Data science and AI: preparing datasets, moving files, launching batch training runs and chaining tools is usually done with the shell.
  • Everyday development: the scripts in a package.json, Makefiles and git hooks all end up invoking the shell.

In all of these contexts, knowing Bash is the difference between "waiting for someone in ops to look at it" and solving it yourself in five minutes.

  1. Our case study: Veloz Envíos and the veloz-ops toolkit

Throughout this course you will not learn Bash with abstract examples, but by solving the problems of a specific company.

Veloz Envíos is a fictional last-mile delivery company operating in Valencia, Sevilla and Bilbao. You join as technical operations lead. Your infrastructure looks like this:

Item Path / name Contents
Main server srv-veloz-01 (Ubuntu 24.04 LTS) Where the internal veloz-api runs
Web access log /var/log/veloz/access.log Combined format: IP, date, request, status code, bytes
Application log /var/log/veloz/app.log Lines like 2026-08-03 10:15:22 [INFO] message
Business data /srv/veloz/data/shipments.csv shipment_id,date,city,courier,status,amount
Your scripts ~/veloz-ops/ With bin/, lib/, etc/, logs/

The real problem we are going to solve is this one. Every morning, someone on the team does the following by hand:

  1. Logs in over SSH to srv-veloz-01.
  2. Checks whether the disk has space and whether veloz-api is alive.
  3. Looks for the ERROR entries of the last 24 hours in app.log.
  4. Counts how many requests returned a 500 error in access.log.
  5. Opens shipments.csv in a spreadsheet to count issues per city.
  6. Copies everything into an email and sends it to operations.

That is around forty minutes a day of repetitive, error-prone work that nobody does when that person is on holiday. It is the perfect candidate for automation.

The solution we will build, piece by piece, is called veloz-ops: a small toolkit of in-house scripts that lives in ~/veloz-ops/ and that, by the end of the course, will be able to generate that report on its own every morning and raise an alert when something goes wrong. Each module contributes a piece:

graph TD
    M1["Modules 1-2<br/>Groundwork: shell and commands"] --> M3["Modules 3-4<br/>First scripts with logic"]
    M3 --> M5["Modules 5-6<br/>Robustness, awk, sed, APIs"]
    M5 --> M7["Module 7<br/>Cron, systemd, remote"]
    M7 --> M8["Module 8<br/>Quality: ShellCheck, tests, Git"]
    M8 --> M9["Module 9<br/>veloz-ops in production"]

Keep this mental picture: everything you learn has a concrete destination. We are not collecting commands, we are building a tool.

  1. Your first commands

Let us finish by touching the keyboard. Open a terminal (if you do not know how yet, lesson 01-02 covers it in detail) and try these three commands.

9.1 echo: writing text on screen

echo "Starting diagnostics for srv-veloz-01"
Starting diagnostics for srv-veloz-01

echo is the simplest command of all: it prints whatever you pass it. It looks trivial, but it is the basis of a script's messages, of reports and of debugging. Notice the structure: the word echo is the command, and the quoted text is its argument. The quotes group several words into a single argument; without them it would work here too, but pick up the habit from the start (the exact reason is explained in 03-06).

9.2 date: the system date and time

date
Mon 03 Aug 2026 10:15:22 AM CEST

By default it shows the date in the format of the system language. But it accepts a format option that starts with +:

date '+%Y-%m-%d'
2026-08-03

Here %Y is the four-digit year, %m the month and %d the day. This YYYY-MM-DD format is not a whim: it sorts alphabetically the same way it sorts chronologically, which is why we will use it to name the log and report files of veloz-ops. A file called report-2026-08-03.txt always ends up in the right place when you list the directory.

9.3 whoami: which identity you are working under

whoami
joan

It answers with the name of the current user. On a shared server like srv-veloz-01 it is one of the first things you check, because permissions depend on who you are: being joan is not the same as being root. Permissions are covered in depth in lesson 02-03.

9.4 Putting it together

echo "Diagnostics for srv-veloz-01 run by $(whoami) on $(date '+%Y-%m-%d')"
Diagnostics for srv-veloz-01 run by joan on 2026-08-03

That $(command) construct is called command substitution: Bash first runs whatever is inside the parentheses and replaces the expression with its output. It is one of the most useful mechanisms in the shell and we will study it thoroughly in 03-06; for now, keep the intuition that it lets you drop the result of a command inside a piece of text.

Common Mistakes and Tips

  • Confusing the terminal with the shell. Switching terminal emulator (from GNOME Terminal to Alacritty, for example) does not change your shell. If you want to know which one you are really using, there are specific tools that we will see in 01-02 and 01-04; do not trust the look of the window.
  • Believing that "Linux" and "Bash" are synonyms. Bash is a program that runs on Linux, and it can also run on macOS or Windows. Conversely, a Linux system can perfectly well use another shell.
  • Writing #!/bin/sh in a script with bashisms. On Ubuntu that invokes dash and you will get errors such as [[: not found. Use #!/usr/bin/env bash whenever you use Bash features.
  • Copying commands from the internet without understanding them. It is the number one cause of disasters on servers. In lesson 01-05 you will learn to check what a command does before running it.
  • Thinking that Bash is "only for administrators". Any technical role working with servers, containers or pipelines needs it. It is one of the skills with the best ratio between learning effort and daily usefulness.
  • A tip on method: do not memorise commands. Memorise concepts (what a shell is, what an expansion is, what an exit code is) and learn to look things up in the documentation. A professional consults man daily without the slightest embarrassment.

Exercises

Exercise 1: Identify the concepts

Without running anything yet, answer in your own words:

  1. What is the difference between a terminal, a shell and the kernel?
  2. Why can a script written for Bash fail if dash runs it?
  3. Name three places in professional life where you will run into Bash even if you are not a systems administrator.

Exercise 2: Your first operations message

Type a single command in the terminal that prints exactly this text (with the date of the day you run it):

[Veloz Envios] Daily report generated on 2026-08-03

Hints: you need echo, the $(...) substitution and date with the +%Y-%m-%d format.

Exercise 3: Choosing the right shell

For each situation, say which shell you would choose and why:

  1. A boot script that must work on Debian, on Alpine Linux and on a router running BusyBox.
  2. Your daily working shell, where you value intelligent completion.
  3. A script that must run in the Veloz Envíos GitHub Actions pipeline and that uses arrays.
  4. Automating mailbox creation on a Windows server.

Solutions

Solution to Exercise 1

  1. The terminal is the window (or the device) that displays text and picks up your keystrokes; the shell is the program that runs inside it and interprets what you type; the kernel is the core of the operating system, which manages hardware, memory and processes. The chain is: you → terminal → shell → kernel → hardware.
  2. Because dash implements only the POSIX standard and does not support Bash's own extensions (arrays, [[ ... ]], brace expansion and so on). If the script uses them, dash does not recognize them and fails. On Ubuntu and Debian, /bin/sh is precisely dash, so the error appears as soon as you carelessly use #!/bin/sh.
  3. For example: the run: steps of a CI/CD pipeline (GitHub Actions, GitLab CI); the RUN instructions and entrypoint.sh scripts of Docker containers; the scripts in a package.json or a Makefile during development. The user-data of cloud instances or dataset preprocessing in data science would also count.

Solution to Exercise 2

echo "[Veloz Envios] Daily report generated on $(date '+%Y-%m-%d')"
[Veloz Envios] Daily report generated on 2026-08-03

A breakdown of what happens:

  1. Bash spots $(date '+%Y-%m-%d') and runs it first.
  2. date returns 2026-08-03.
  3. Bash replaces the whole expression with that text, leaving a single string.
  4. echo prints the resulting string.

The double quotes are necessary here for two reasons: they keep the text as a single argument and, at the same time, they allow the $(...) substitution to happen. Had you used single quotes, you would see $(date '+%Y-%m-%d') literally on screen. That difference is explained in detail in lesson 03-06.

Solution to Exercise 3

  1. POSIX sh (which on Alpine and BusyBox will be ash, and on Debian dash). The requirement is maximum portability, so you have to give up bashisms. That is the case we will handle in 08-07.
  2. zsh or fish, for their superior completion and ergonomics. These are personal comfort decisions; they do not affect scripts, which will still be Bash.
  3. bash, with #!/usr/bin/env bash. Arrays are a Bash extension and the GitHub Actions runners ship with it installed by default.
  4. PowerShell, because the Windows and Active Directory administration ecosystem is built on its cmdlets and its object model.

Conclusion

You now know what Bash is and why it deserves your time: it is the shell that wraps the kernel, the direct heir of the Bourne shell, and the de facto standard on servers, containers and pipelines. You have seen its two faces — conversing with the system in interactive mode and automating it in script mode — how it compares with sh, dash, zsh, fish and PowerShell, and at which moments of your professional life it will show up. And, above all, you have met Veloz Envíos and the concrete problem we will solve: those forty daily minutes of manual reporting that will end up turned into the veloz-ops toolkit.

To start working you need a proper environment: Bash installed and in a modern version, a comfortable terminal, your configuration files under control and the project's directory skeleton created. That is exactly what you will do in the next lesson, Setting Up Your Environment.

Bash Programming Course

Module 1: Introduction to Bash

Module 2: Basic Bash Commands

Module 3: Scripting Fundamentals

Module 4: Intermediate Scripting

Module 5: Advanced Scripting Techniques

Module 6: Working with External Tools

Module 7: Automation and Scheduling

Module 8: Best Practices and Optimization

Module 9: Real-World Projects

© Copyright 2026. All rights reserved