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
- What a shell is and what it actually does
- Bash within the Unix shell family
- A short history: from Thompson to Bourne Again
- Why Bash is still the de facto standard
- The two uses of Bash: interactive interpreter and scripting language
- Comparison: Bash versus other shells
- Where you will run into Bash professionally
- Our case study: Veloz Envíos and the
veloz-opstoolkit - Your first commands
- 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:
- It displays a prompt (an invitation to type).
- It reads the line you type.
- It interprets it: it decides which command you want to run and with which data.
- It runs that command and waits for it to finish.
- 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.
- 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 descendksh,bash,dashandzsh. 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).
- 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.
- 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,systemdunits,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.
- 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.
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.
- 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/shisdash, 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/bashis 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.
- 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
RUNin aDockerfileis a shell line, and theentrypoint.shscripts that prepare a container before starting the application are Bash scripts. - Cloud: the
user-dataof an EC2 instance, the boot scripts of an Azure or GCP VM, and much of theaws/gcloud/azdocumentation 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
scriptsin apackage.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.
- Our case study: Veloz Envíos and the
veloz-ops toolkit
veloz-ops toolkitThroughout 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:
- Logs in over SSH to
srv-veloz-01. - Checks whether the disk has space and whether
veloz-apiis alive. - Looks for the
ERRORentries of the last 24 hours inapp.log. - Counts how many requests returned a 500 error in
access.log. - Opens
shipments.csvin a spreadsheet to count issues per city. - 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.
- 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 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
By default it shows the date in the format of the system language. But it accepts a format option that starts with +:
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
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
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/shin a script with bashisms. On Ubuntu that invokesdashand you will get errors such as[[: not found. Use#!/usr/bin/env bashwhenever 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
mandaily without the slightest embarrassment.
Exercises
Exercise 1: Identify the concepts
Without running anything yet, answer in your own words:
- What is the difference between a terminal, a shell and the kernel?
- Why can a script written for Bash fail if
dashruns it? - 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):
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:
- A boot script that must work on Debian, on Alpine Linux and on a router running BusyBox.
- Your daily working shell, where you value intelligent completion.
- A script that must run in the Veloz Envíos GitHub Actions pipeline and that uses arrays.
- Automating mailbox creation on a Windows server.
Solutions
Solution to Exercise 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.
- Because
dashimplements only the POSIX standard and does not support Bash's own extensions (arrays,[[ ... ]], brace expansion and so on). If the script uses them,dashdoes not recognize them and fails. On Ubuntu and Debian,/bin/shis preciselydash, so the error appears as soon as you carelessly use#!/bin/sh. - For example: the
run:steps of a CI/CD pipeline (GitHub Actions, GitLab CI); theRUNinstructions andentrypoint.shscripts of Docker containers; thescriptsin apackage.jsonor aMakefileduring development. Theuser-dataof cloud instances or dataset preprocessing in data science would also count.
Solution to Exercise 2
A breakdown of what happens:
- Bash spots
$(date '+%Y-%m-%d')and runs it first. datereturns2026-08-03.- Bash replaces the whole expression with that text, leaving a single string.
echoprints 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
- POSIX
sh(which on Alpine and BusyBox will beash, and on Debiandash). The requirement is maximum portability, so you have to give up bashisms. That is the case we will handle in 08-07. zshorfish, for their superior completion and ergonomics. These are personal comfort decisions; they do not affect scripts, which will still be Bash.bash, with#!/usr/bin/env bash. Arrays are a Bash extension and the GitHub Actions runners ship with it installed by default.- 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
- What Is Bash?
- Setting Up Your Environment
- Basic Command Line Navigation
- Understanding the Shell
- Finding Help: man, help and --help
Module 2: Basic Bash Commands
- File and Directory Operations
- Text Processing Commands
- File Permissions and Ownership
- Redirection and Piping
- Wildcards and Path Expansion
- History and Keyboard Shortcuts
Module 3: Scripting Fundamentals
- Creating and Running a Script
- Variables and Constants
- Basic Operators
- Conditional Statements
- Arguments and User Input
- Quoting, Expansion and Substitution
Module 4: Intermediate Scripting
- Loops in Bash
- Functions in Bash
- Arrays and Associative Arrays
- String Manipulation
- The case Statement and Interactive Menus
- Arithmetic and Numeric Calculations
Module 5: Advanced Scripting Techniques
- Advanced File Operations
- Process Management
- Error Handling and Debugging
- Regular Expressions
- Advanced I/O: Descriptors and Here-Documents
- Modular Scripts and Reusable Libraries
Module 6: Working with External Tools
Module 7: Automation and Scheduling
- Cron Jobs
- Automating Tasks
- Backup and Restore Scripts
- Monitoring and Logging
- Services and Timers with systemd
- Remote Automation with SSH
Module 8: Best Practices and Optimization
- Writing Readable Code
- Optimizing Bash Scripts
- Security Considerations
- Version Control with Git
- Static Analysis with ShellCheck and shfmt
- Automated Testing with Bats
- Portability: POSIX sh versus Bashisms
