What Is BASH? Understanding the Linux Shell

Bash (Bourne Again SHell) is the default command-line shell on most Linux distributions. A shell is a program that reads commands you type, interprets them, and communicates them to the operating system. When you open a terminal and type ls or cd, Bash is the program receiving your input, finding the right programs to run, and displaying the results. Beyond running commands, Bash is also a complete programming language for writing scripts that automate tasks.

The Program Behind the Prompt

When you open a terminal on a Linux system and see a prompt like sarah@mycomputer:~$, you are looking at Bash waiting for your input. Every command you type is read and processed by Bash. Every time you press Enter, Bash interprets what you wrote, finds the appropriate programs, passes your arguments to them, and shows you the results.

Bash is simultaneously the most-used program on Linux and one of the least-noticed. It sits between you and the operating system, translating human-readable commands into the system calls and program executions that actually make things happen. When Bash works well — which is almost always — it is invisible. When something unexpected happens, understanding how Bash works is what lets you diagnose and fix it.

New Linux users often conflate the terminal, the shell, and the command line. These are related but distinct concepts. The terminal is the window or program that provides the text display. The shell is the program running inside that terminal interpreting your commands. The command line is the interface style — text input rather than graphical interaction. Bash is a shell: one specific program in a family of programs that have served this role across Unix and Linux history.

This article explains what Bash is, how it works, its key features, how it relates to the broader world of Linux shells, and how to begin customizing it to suit your workflow. Whether you have been using Bash for years without fully understanding it or are encountering it for the first time, this foundation makes everything else about Linux terminal usage clearer.

The Shell: Linux’s Command Interpreter

What a Shell Does

A shell performs several distinct functions that together create the interactive command-line experience:

Command reading — the shell displays a prompt, reads what you type, and detects when you have finished a complete command (pressed Enter, or matched all open parentheses and quotes in a multi-line command).

Parsing and expansion — before executing anything, the shell processes your input through several expansion steps: expanding variables ($HOME becomes /home/sarah), expanding wildcards (*.txt becomes the list of matching filenames), processing command substitution (`date` or $(date) runs date and inserts its output), and splitting the result into tokens.

Locating programs — after parsing, the shell searches the PATH to find the executable matching the command name.

Execution — the shell forks a child process and executes the program within it, passing arguments and setting up input/output redirections.

Output management — the shell handles pipes (|), redirections (>, <, >>), and connecting multiple commands together.

Environment management — the shell maintains variables, aliases, and functions that affect how commands run.

Script execution — when given a script file, the shell reads and executes its contents line by line, supporting conditionals, loops, functions, and all the constructs of a programming language.

Shells Are Just Programs

A critical and often surprising point for new Linux users: the shell is just a regular program. It has no special relationship with the operating system — it uses the same system calls any other program uses to create processes, read files, and write output. The shell’s only special status is that it is the first program started after you log in (or open a terminal), and it runs your other programs for you.

This means you can have multiple shells running simultaneously, you can switch between shells, and you can write scripts that specify which shell should execute them.

A Brief History: From sh to Bash

Understanding where Bash comes from explains its name, its quirks, and why certain features exist.

The Original Shell: sh (Thompson Shell → Bourne Shell)

The first Unix shell, written by Ken Thompson at Bell Labs in 1971, established the basic model that all later shells follow: read a command, execute it, show the result, repeat.

In 1979, Steve Bourne rewrote the shell, creating sh — the Bourne Shell. The Bourne Shell introduced most of the scripting features we still use today: variables, control structures (if, while, for), functions, and I/O redirection. Its syntax became the standard for Unix shell scripting.

The C Shell and ksh

The C shell (csh), developed by Bill Joy at UC Berkeley in the late 1970s, introduced interactive features that sh lacked: command history, aliases, and job control. But csh‘s scripting syntax differed significantly from Bourne shell, creating a split between interactive shells and scripting shells that persisted for years.

The Korn Shell (ksh), developed by David Korn at AT&T in the 1980s, aimed to combine Bourne shell compatibility with C shell interactive features. It influenced nearly all later shells.

Bash: Bourne Again SHell

When the GNU Project set out to create a free Unix-compatible operating system in the 1980s, it needed a free replacement for the Bourne shell. Brian Fox wrote Bash (Bourne Again SHell) for the GNU Project, first released in 1989. The name is a deliberate pun on “Bourne Again” — Bash is both a rewrite of the Bourne shell and a “born again” revival of it.

Bash combined:

  • Full Bourne shell (sh) compatibility — scripts written for sh work in Bash
  • Interactive features from csh and ksh — history, tab completion, aliases
  • New features — arithmetic expansion, arrays, improved string manipulation
  • GNU-style improvements — long options, better error messages, comprehensive documentation

When Linux was created in the early 1990s, it adopted Bash as its default shell. Today, Bash is the default interactive shell on Ubuntu, Fedora, Debian, and most other Linux distributions, and the default shell for shell scripts across the Linux ecosystem.

Terminal vs. Shell vs. Console: Clearing Up the Confusion

These terms are related but distinct, and the distinction matters for understanding how the command-line environment is structured.

Terminal Emulator

A terminal emulator (often just called “terminal”) is the graphical application that provides a window for text-based interaction. Examples:

  • GNOME Terminal (default on Ubuntu with GNOME)
  • Konsole (default on KDE Plasma)
  • xterm (lightweight, classic)
  • Alacritty, Kitty (modern GPU-accelerated)
  • Windows Terminal (on Windows Subsystem for Linux)

The terminal emulator handles rendering text on screen, capturing keyboard input, managing colors and fonts, and providing copy-paste functionality. It does not interpret commands — that is the shell’s job.

Shell

The shell is the program running inside the terminal that interprets commands. The terminal emulator starts a shell when it opens. Common shells:

  • Bash — the most common Linux default
  • Zsh — popular alternative, default on macOS
  • Fish — user-friendly with excellent auto-suggestions
  • Dash — minimal POSIX shell, used for system scripts
  • ksh — Korn shell, common in enterprise Unix/Linux

Console

A console (or virtual console) is a text-mode terminal built into the Linux kernel, accessible without a graphical desktop. Press Ctrl+Alt+F2 (or F3 through F6) on most Linux systems to switch to a virtual console — a full-screen text terminal. Ctrl+Alt+F1 (or F7 on some systems) returns to the graphical desktop.

Consoles are useful when the graphical desktop crashes or fails to start — you can still log in, diagnose problems, and repair the system.

How They Connect

You type → Terminal Emulator captures keystroke
         → Terminal Emulator sends character to Shell (Bash)
         → Bash reads the character, adds it to the input buffer
         → You press Enter
         → Bash parses and executes the command
         → Program output goes to Bash's stdout
         → Bash passes output to Terminal Emulator
         → Terminal Emulator renders the text on screen

Bash Features Every User Should Know

Command History

Bash records every command you run in a history file (~/.bash_history). Navigate and reuse history with:

↑ / ↓          Navigate through previous commands
Ctrl+R         Reverse search — type to search history interactively
!!             Repeat the last command
!n             Repeat command number n from history
!string        Repeat the most recent command starting with 'string'
!$             The last argument of the previous command

Reverse search (Ctrl+R) is one of the most valuable Bash shortcuts:

(reverse-i-search)`git': git commit -m "Fix bug in parser"

Type characters and Bash progressively narrows to matching history entries. Press Enter to run the found command, or Ctrl+R again to find an older match.

View history:

$ history
  498  ls -la
  499  cd ~/projects
  500  git status
  501  git add .
  502  git commit -m "Fix bug in parser"

Set history size (in ~/.bashrc):

HISTSIZE=10000        # Commands to keep in memory
HISTFILESIZE=20000    # Commands to keep in the file

Tab Completion

Press Tab while typing a command or filename to have Bash complete it automatically. This works for:

  • Command names (type git s + Tab → completes to git status if unambiguous)
  • Filenames and directories (type ~/Doc + Tab → ~/Documents/)
  • Command options (with bash-completion package installed)
  • Hostnames, usernames, and more

Double-Tab when there are multiple completions shows all possibilities:

$ git s[TAB][TAB]
send-email  shortlog    show        show-branch stash       status      submodule

Install enhanced tab completion:

$ sudo apt install bash-completion

And ensure it is enabled in ~/.bashrc:

if [ -f /etc/bash_completion ]; then
    . /etc/bash_completion
fi

Aliases: Command Shortcuts

Aliases let you create short names for longer commands or set default options for existing commands:

alias ll='ls -la'
alias la='ls -A'
alias ..='cd ..'
alias ...='cd ../..'
alias grep='grep --color=auto'
alias df='df -h'
alias free='free -h'
alias update='sudo apt update && sudo apt upgrade'

Define aliases in ~/.bashrc to make them permanent. View all current aliases:

$ alias
alias grep='grep --color=auto'
alias ll='ls -la'
alias ls='ls --color=auto'

Remove an alias for the current session:

$ unalias ll

Variables and Environment Variables

Bash distinguishes between shell variables (available only in the current shell) and environment variables (exported to child processes):

# Shell variable (not inherited by programs you run)
my_name="Sarah"
echo $my_name         # Sarah

# Environment variable (inherited by child processes)
export MY_PROJECT="/home/sarah/projects/webapp"
echo $MY_PROJECT      # /home/sarah/projects/webapp

Important built-in variables:

$HOME        # Your home directory: /home/sarah
$USER        # Your username: sarah
$SHELL       # Your current shell: /bin/bash
$PATH        # Directory search path
$PWD         # Current working directory
$OLDPWD      # Previous working directory (used by cd -)
$?           # Exit status of the last command (0=success)
$$           # PID of the current shell
$RANDOM      # A random integer between 0 and 32767
$LINENO      # Current line number (in scripts)

Redirection: Controlling Input and Output

Bash handles three standard streams for every command:

  • stdin (0) — standard input (default: keyboard)
  • stdout (1) — standard output (default: terminal)
  • stderr (2) — standard error (default: terminal)
# Redirect stdout to a file (overwrite)
$ ls -la > file_list.txt

# Redirect stdout to a file (append)
$ ls -la >> file_list.txt

# Redirect stderr to a file
$ make 2> build_errors.txt

# Redirect both stdout and stderr to a file
$ command > output.txt 2>&1
$ command &> output.txt          # Bash shorthand for the same

# Redirect stdin from a file
$ sort < unsorted.txt

# Discard output (send to null device)
$ noisy_command > /dev/null
$ noisy_command 2> /dev/null     # Discard only errors
$ noisy_command &> /dev/null     # Discard everything

Pipes: Connecting Commands

The pipe operator | connects the stdout of one command to the stdin of the next, creating a processing pipeline:

# Count files in current directory
$ ls | wc -l

# Find and display large files
$ find ~ -size +100M | sort

# Show the 5 most memory-hungry processes
$ ps aux --sort=-%mem | head -6

# Search for errors in logs and count occurrences
$ grep "error" /var/log/syslog | wc -l

# Multi-stage pipeline
$ cat /etc/passwd | grep -v "nologin" | cut -d: -f1 | sort

Job Control: Background and Foreground Processes

Bash manages jobs — commands you have started from the shell:

# Run a command in the background
$ long_running_command &
[1] 12345

# See all current jobs
$ jobs
[1]+  Running    long_running_command &

# Bring background job to foreground
$ fg %1

# Suspend foreground job (send to background paused)
$ Ctrl+Z
[1]+  Stopped    long_running_command

# Resume suspended job in background
$ bg %1

# Kill a background job
$ kill %1

Command Substitution

Insert the output of one command as an argument to another:

# Modern syntax (preferred)
$ echo "Today is $(date)"
Today is Thu Feb 18 10:35:42 UTC 2026

$ mkdir backup_$(date +%Y%m%d)
# Creates: backup_20260218

# Legacy syntax (still works)
$ echo "Kernel: `uname -r`"
Kernel: 6.8.0-51-generic

Command substitution works anywhere in a command line — in assignments, as arguments, in conditions.

Arithmetic Expansion

Bash performs integer arithmetic with $(( )):

$ echo $((2 + 3))
5

$ echo $((10 * 5 - 3))
47

$ files=$(ls | wc -l)
$ echo "There are $((files * 2)) items if doubled"

$ x=10
$ echo $((x++))    # Uses x, then increments
10
$ echo $x
11

The Bash Startup Files

Bash reads configuration files at startup to set up your environment. Understanding which files are read when determines where to put your customizations.

Login Shells vs. Interactive Non-Login Shells

Login shell — started when you first log in (SSH session, virtual console login, bash --login). Reads:

  1. /etc/profile — system-wide configuration
  2. ~/.bash_profile or ~/.bash_login or ~/.profile — user configuration (first found)

Interactive non-login shell — started when you open a terminal emulator (GNOME Terminal, etc.) after already being logged in. Reads:

  1. /etc/bash.bashrc — system-wide Bash configuration
  2. ~/.bashrc — user Bash configuration

On Ubuntu, ~/.profile sources ~/.bashrc, so both types of shell end up reading ~/.bashrc. For most customization purposes, ~/.bashrc is the right place.

What to Put in ~/.bashrc

~/.bashrc is your primary shell customization file. Common additions:

# Custom aliases
alias ll='ls -la --color=auto'
alias gs='git status'
alias glog='git log --oneline --graph'

# PATH additions
export PATH="$HOME/.local/bin:$PATH"
export PATH="$HOME/.cargo/bin:$PATH"

# Environment variables
export EDITOR=nano
export VISUAL=nano

# Custom prompt (PS1)
export PS1='\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '

# History settings
HISTSIZE=10000
HISTFILESIZE=20000
HISTCONTROL=ignoredups:erasedups

# Enable bash completion
if [ -f /etc/bash_completion ]; then
    . /etc/bash_completion
fi

# Load local machine-specific settings if present
if [ -f ~/.bashrc.local ]; then
    . ~/.bashrc.local
fi

After editing ~/.bashrc, apply changes to the current session:

$ source ~/.bashrc
# or equivalently:
$ . ~/.bashrc

The Bash Prompt: PS1

The prompt displayed before each command is controlled by the PS1 variable. The default on Ubuntu looks like sarah@mycomputer:~$ — but it can be customized extensively.

Prompt Escape Sequences

Sequence Meaning
\u Current username
\h Hostname (short)
\H Hostname (full)
\w Current working directory (full path)
\W Current working directory (basename only)
\$ $ for regular user, # for root
\t Current time (HH:MM:SS)
\T Current time (12-hour)
\d Date (day month date)
\n Newline
\[ and \] Wrap non-printing sequences (like colors)

Adding Color to the Prompt

ANSI escape codes add color:

# Green username@host, blue directory
export PS1='\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '

Color codes: \033[01;32m = bold green, \033[01;34m = bold blue, \033[00m = reset.

Showing Git Branch in the Prompt

Many developers add the current git branch to the prompt:

# Add to ~/.bashrc
parse_git_branch() {
    git branch 2>/dev/null | grep '\*' | sed 's/\* //'
}

export PS1='\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\[\033[01;33m\]$(git_branch_or_empty)\[\033[00m\]\$ '

git_branch_or_empty() {
    branch=$(parse_git_branch)
    [ -n "$branch" ] && echo " ($branch)"
}

Result: sarah@mycomputer:~/projects/webapp (main)$

Other Linux Shells

Bash is the most common Linux shell but not the only one. Knowing the alternatives prepares you for environments where Bash is not the default and helps you choose whether to switch.

Zsh (Z Shell)

Zsh has been the default shell on macOS since Catalina (2019) and is popular among Linux power users. It is highly compatible with Bash while offering:

  • Better tab completion with menu-driven selection
  • More powerful globbing patterns
  • Spell correction for mistyped commands
  • The oh-my-zsh framework with hundreds of themes and plugins
  • Right-side prompt (RPROMPT)
$ sudo apt install zsh
$ chsh -s /usr/bin/zsh    # Change default shell

Fish (Friendly Interactive Shell)

Fish prioritizes usability for interactive use:

  • Syntax highlighting as you type (valid commands in blue, errors in red)
  • Auto-suggestions based on history (press right arrow to accept)
  • Consistent, learnable syntax
  • No configuration needed for a great out-of-box experience
$ sudo apt install fish

Note: Fish is not POSIX-compatible — Bash scripts do not run in Fish without modification. Fish is excellent for interactive use but keep Bash for scripting.

Dash

Dash (Debian Almquist Shell) is a minimal POSIX shell optimized for script execution speed. On Ubuntu, /bin/sh points to Dash rather than Bash. System startup scripts run under Dash for performance — it starts faster and uses less memory than Bash.

Write portable scripts using only POSIX sh features (avoid Bash-specific extensions) and start them with #!/bin/sh if you want them to use Dash. Use #!/bin/bash for scripts that use Bash-specific features.

Checking and Changing Your Shell

$ echo $SHELL           # Current login shell
/bin/bash

$ cat /etc/shells       # Available shells on the system
/bin/sh
/bin/bash
/usr/bin/bash
/bin/zsh
/usr/bin/zsh
/usr/bin/fish

$ chsh -s /usr/bin/zsh  # Change login shell (takes effect at next login)

Bash Scripting: The Other Half

Bash is not just an interactive tool — it is a complete programming language. Shell scripts automate repetitive tasks, orchestrate complex workflows, and form the backbone of Linux system administration.

The Basics of a Bash Script

#!/bin/bash
# The shebang line tells the system which interpreter to use

# Variables
project_dir="/home/sarah/projects"
backup_dir="/home/sarah/backups"
date_stamp=$(date +%Y%m%d)

# Conditional
if [ -d "$project_dir" ]; then
    echo "Project directory exists"
else
    echo "Project directory not found"
    exit 1
fi

# Create backup
tar -czf "$backup_dir/backup_$date_stamp.tar.gz" "$project_dir"

# Check if backup succeeded
if [ $? -eq 0 ]; then
    echo "Backup completed: backup_$date_stamp.tar.gz"
else
    echo "Backup failed!" >&2
    exit 1
fi

The Shebang Line

The first line #!/bin/bash is the shebang (sharp-bang, or hash-bang). It tells the kernel which program should interpret this script file. When you run ./script.sh, the kernel reads this line and executes /bin/bash with the script as input.

Common shebangs:

  • #!/bin/bash — use Bash (with Bash-specific features)
  • #!/bin/sh — use the system’s POSIX sh (more portable)
  • #!/usr/bin/env bash — find bash in PATH (more portable across systems)
  • #!/usr/bin/python3 — a Python script
  • #!/usr/bin/perl — a Perl script

Making a Script Executable and Running It

$ chmod +x script.sh     # Make executable
$ ./script.sh            # Run from current directory
$ bash script.sh         # Run without execute permission (bash interprets directly)

Bash scripting is a deep topic — loops, functions, arrays, string manipulation, process substitution, and more — worthy of its own dedicated articles. This introduction establishes the connection between Bash as an interactive shell and Bash as a scripting language: they are the same program used in two different modes.

Essential Bash Keyboard Shortcuts

Shortcut Action
Cursor Movement
Ctrl+A Move to beginning of line
Ctrl+E Move to end of line
Ctrl+F Move forward one character
Ctrl+B Move backward one character
Alt+F Move forward one word
Alt+B Move backward one word
Editing
Ctrl+K Delete from cursor to end of line
Ctrl+U Delete from cursor to beginning of line
Ctrl+W Delete word before cursor
Alt+D Delete word after cursor
Ctrl+Y Paste (yank) deleted text
Ctrl+_ Undo
History
↑ / ↓ Navigate history
Ctrl+R Reverse history search
Ctrl+G Cancel history search
!! Last command
!$ Last argument of last command
Control
Ctrl+C Interrupt (kill) current process
Ctrl+D EOF / logout
Ctrl+Z Suspend current process
Ctrl+L Clear screen
Tab Complete command or filename
Tab Tab Show all completions

Conclusion: Bash as the Foundation

Bash is not just a way to run commands — it is the environment in which Linux administration, scripting, and development happen. Every feature described in this article — history, completion, aliases, variables, redirection, pipes, job control — exists to make that environment more powerful and productive.

Understanding Bash at this level transforms the command line from a place where you type commands and hope for the best into a responsive, customizable tool you control deliberately. The prompt reflects your customizations. The aliases reflect your workflow shortcuts. The history reflects your work. The scripts you write in Bash reflect your ability to automate and systematize what would otherwise be manual labor.

Bash is the first program you interact with in Linux and the one you will interact with more than any other. Knowing it well is not a niche skill — it is foundational to everything Linux offers.

Hot this week

What Are Snap, Flatpak, and AppImage Packages?

Learn what Snap, Flatpak, and AppImage are, how universal Linux packages work, their differences, pros and cons, and when to use each format for installing software.

How to Change Your Desktop Environment in Linux

Learn how to install and switch desktop environments in Linux — from GNOME to KDE Plasma, XFCE, MATE, Cinnamon, and more. Includes installation steps and how to choose the right DE.

Understanding Linux System Directories: What Goes Where?

Learn the Linux filesystem hierarchy — what each directory like /etc, /var, /usr, /home, /tmp, /proc, and /dev contains and why the system is organized this way.

What Is a Symbolic Link in Linux?

Learn what symbolic links (symlinks) are in Linux, the difference between soft and hard links, how to create them with ln, and practical uses for symlinks in system administration.

How to Mount and Unmount Drives in Linux

Learn how to mount and unmount drives, USB drives, and partitions in Linux using the mount command, /etc/fstab for persistent mounts, and how to safely eject external drives.

Topics

What Are Snap, Flatpak, and AppImage Packages?

Learn what Snap, Flatpak, and AppImage are, how universal Linux packages work, their differences, pros and cons, and when to use each format for installing software.

How to Change Your Desktop Environment in Linux

Learn how to install and switch desktop environments in Linux — from GNOME to KDE Plasma, XFCE, MATE, Cinnamon, and more. Includes installation steps and how to choose the right DE.

Understanding Linux System Directories: What Goes Where?

Learn the Linux filesystem hierarchy — what each directory like /etc, /var, /usr, /home, /tmp, /proc, and /dev contains and why the system is organized this way.

What Is a Symbolic Link in Linux?

Learn what symbolic links (symlinks) are in Linux, the difference between soft and hard links, how to create them with ln, and practical uses for symlinks in system administration.

How to Mount and Unmount Drives in Linux

Learn how to mount and unmount drives, USB drives, and partitions in Linux using the mount command, /etc/fstab for persistent mounts, and how to safely eject external drives.

Understanding the Linux Man Pages: Your Built-in Documentation

Learn how to use Linux man pages to look up any command, understand its options, and navigate the built-in documentation system. Includes man, info, apropos, and whatis.

How to View Running Processes in Linux

Learn how to view and manage running processes in Linux using ps, top, htop, pgrep, and pstree. Understand process states, PIDs, CPU/memory usage, and how to find and kill processes.

How to Check Your Linux System Information

Learn how to check Linux system information including OS version, kernel, CPU, RAM, disk space, network, and hardware details using essential terminal commands.

Related Articles

Popular Categories