The Bash Shell
What Is a Shell?
The shell is your command-line interpreter. Bash (Bourne Again Shell) is the default on most Linux distros. It parses input, expands variables, runs external programs, and supports full scripting.
echo $SHELL # /bin/bash
echo $0 # bash (interactive session)
bash --version
# Alternative shells
cat /etc/shells
chsh -l # list available
Prompt and Navigation
cd /var/log
pwd # print working directory
cd - # previous directory (directory stack)
pushd /tmp && popd # explicit stack
# Customize prompt (temporary)
export PS1='\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '
Persist prompt changes in ~/.bashrc. Login shells may read ~/.bash_profile or ~/.profile first.
History and Completion
history # list past commands
history | grep apt # search history file
!! # repeat last command
!grep # last command starting with grep
!123 # command number 123 from history
# Interactive search: Ctrl+R (reverse), Ctrl+S (forward)
# Accept match: Enter or Right arrow
Tab completion uses bash-completion package for flags and service names:
systemctl sta<Tab> # start, status, stop, ...
apt install ngi<Tab>
Enable programmable completion in ~/.bashrc:
if [ -f /etc/bash_completion ]; then
. /etc/bash_completion
fi
Pipes and Redirection
| Operator | Meaning |
|---|---|
| |
Pipe stdout to stdin of next command |
> |
Redirect stdout (overwrite) |
>> |
Redirect stdout (append) |
2> |
Redirect stderr |
&> |
Redirect stdout and stderr |
< |
Redirect stdin from file |
2>&1 |
Merge stderr into stdout |
# Count error lines in nginx log
grep "error" /var/log/nginx/error.log | wc -l
# Save stdout and stderr separately
make > build.log 2> build-errors.log
# Append both streams
./deploy.sh &>> deploy.log
# Pipe stderr too
cmd 2>&1 | grep -i error
Order matters: cmd > file 2>&1 captures both streams; cmd 2>&1 > file only redirects stdout to file.
Environment Variables
export EDITOR=vim
export PATH="$HOME/bin:$PATH"
export LANG=en_US.UTF-8
# View all exported
env | sort
printenv HOME
# Persist
echo 'export EDITOR=vim' >> ~/.bashrc
source ~/.bashrc
Common variables: HOME, USER, PATH, LANG, PS1, HISTSIZE, HISTFILESIZE.
# Extend PATH safely (prepend user bin)
export PATH="$HOME/.local/bin:$PATH"
Quoting and Escaping
echo "Hello $USER" # variable expanded
echo 'Hello $USER' # literal $USER
echo $'line1\nline2' # ANSI-C quoting: escape sequences
# Command substitution
today=$(date +%Y-%m-%d)
files=$(ls *.md 2>/dev/null)
count=$(wc -l < access.log)
# Nested substitution
branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown")
Always quote variables in scripts: "$var" prevents word splitting on spaces.
Job Control
sleep 100 &
jobs # list background jobs
fg %1 # bring job 1 to foreground
bg %1 # resume stopped job in background
kill %1 # terminate job 1
wait # wait for all background jobs
Useful Shortcuts
| Shortcut | Action |
|---|---|
| Ctrl+C | Interrupt current command (SIGINT) |
| Ctrl+Z | Suspend current command |
| Ctrl+D | End of input / logout |
| Ctrl+L | Clear screen |
| Ctrl+A / Ctrl+E | Beginning / end of line |
| Alt+. | Insert last argument of previous command |
| Ctrl+R | Reverse history search |
Best Practices
| Practice | Reason |
|---|---|
| Quote variables in scripts | Prevents word splitting and glob expansion |
Use $(cmd) not backticks |
Readable nesting, no escape hell |
Set HISTTIMEFORMAT |
Audit when commands ran |
| Avoid spaces in paths | Or always quote paths |
export HISTTIMEFORMAT='%F %T '
export HISTCONTROL=ignoredups:erasedups
Common Mistakes
| Mistake | Consequence |
|---|---|
Unquoted $var with spaces |
Breaks filenames, deletes wrong files |
Piping to sh without review |
Executes untrusted remote scripts |
source vs ./script confusion |
source runs in current shell; ./ in subshell |
Forgetting export |
Child processes don’t see variable |
Troubleshooting
Tab completion broken: Install bash-completion package and source it in ~/.bashrc.
History not saving: Check HISTFILE permissions and shopt histappend for concurrent sessions.
Wrong shell after login: Verify chsh -s /bin/bash and /etc/passwd shell field.
Production Scenario
An SRE debugs a failing deploy script over SSH:
# Run with trace to see every command
bash -x ./deploy.sh 2>&1 | tee deploy-trace.log
# Check what the script inherited
env | grep -E '^(PATH|HOME|USER)='
Tracing reveals an unquoted $APP_DIR that breaks when the path contains spaces — fixed with "$APP_DIR" before the next production run.
Bash is the glue between you and every Linux tool — invest time in pipes, history, quoting, and job control before writing automation scripts.