Skip to content
FLAVIO COPES
flaviocopes.com
2026

The Bash shell

By

Learn the Bash shell: navigate files, run commands, quote paths, use variables, pipes and redirection, manage jobs, customize Bash, and write portable scripts.

~~~

Bash is a shell and command-language interpreter.

It reads commands interactively in a terminal and can execute the same language from script files. Bash is common on Linux and servers, but it is not the default interactive shell everywhere. Current macOS versions use zsh by default.

Start Bash explicitly with:

bash

Check the running Bash release:

bash --version

The GNU Bash manual is the authoritative reference for its syntax and built-ins.

Shell commands and external programs

Some commands, including cd, alias, and history, are implemented by Bash itself.

Others are separate programs found through the PATH environment variable:

command -v ls
command -v node

command -v is more portable for this job than which or whereis.

Run a command with arguments:

ls -la ~/Documents

Use the manual or built-in help when you do not recognize an option:

man ls
help cd

Print the current directory:

pwd

List its contents:

ls

Change directory:

cd ~/Documents

Move to the parent directory:

cd ..

Return home:

cd

Create a directory:

mkdir experiments

Use Tab completion for command and file names. Use the Up and Down arrows to move through command history.

Quote paths and values

Spaces and characters such as *, $, &, and ; have meaning to the shell.

Quote a path that contains spaces:

cd "$HOME/My Projects"

Single quotes preserve every character literally:

printf '%s\n' '$HOME'

Double quotes still allow variable and command expansion:

printf '%s\n' "$HOME"

Quote variable expansions unless you intentionally want field splitting or wildcard expansion:

rm -- "$filename"

The -- tells supporting commands that later values are operands, not options. This helps with filenames beginning with -.

Work with files

Common utilities include:

touch notes.txt
cp notes.txt notes-copy.txt
mv notes-copy.txt archive.txt
cat notes.txt
rm archive.txt

rm normally removes files without moving them to a trash folder. Verify paths before using recursive or forced removal.

Wildcards are expanded by the shell before the command runs:

ls *.txt

If no name matches, Bash’s default behavior may pass the pattern unchanged. Do not build destructive commands on an unchecked wildcard.

Variables and environment variables

Assign a shell variable with no spaces around =:

project_name='my app'

Read it:

printf '%s\n' "$project_name"

Export a variable so programs started by the shell inherit it:

export API_URL='https://example.com'

Frequently used environment variables include:

Never print secrets merely to check that they exist. Prefer a test that does not reveal the value:

if [[ -n ${API_KEY:-} ]]; then
  printf '%s\n' 'API_KEY is set'
fi

Update PATH

PATH is a colon-separated list of directories.

Add a personal binary directory for the current shell:

export PATH="$HOME/bin:$PATH"

There must be no spaces around =.

Add that line to the appropriate Bash startup file if you want it in future sessions. Do not overwrite the existing PATH unless you intend to remove the system command directories.

Pipes and redirection

Send one command’s standard output to another command’s standard input:

printf '%s\n' apple banana avocado | grep '^a'

Write standard output to a file, replacing its contents:

printf '%s\n' hello > output.txt

Append instead:

printf '%s\n' again >> output.txt

Redirect standard error:

some-command 2> errors.txt

Redirect both standard output and standard error in Bash:

some-command > output.txt 2>&1

Bash also supports &> output.txt, but > output.txt 2>&1 works in more Bourne-style shells.

Combine commands

Run the second command only if the first succeeds:

npm install && npm test

Run the second only if the first fails:

npm test || printf '%s\n' 'Tests failed'

Run commands sequentially regardless of status:

cd /tmp; pwd

Use && for dependent setup steps. A semicolon can hide a failure and let later commands run in the wrong directory.

Jobs and foreground processes

A foreground command controls the terminal until it exits.

Press Control + C to send it an interrupt signal.

End a command with & to start it as a background job:

long-command &

List jobs started by the current shell:

jobs

Bring job 1 to the foreground:

fg %1

Use a job specification such as %1 with fg and bg, not an operating-system process ID.

Stopping a command with Control + Z does not terminate it. Resume it in the background with bg, bring it back with fg, or terminate it intentionally.

Command history

Show the current history:

history

Search backward interactively with Control + R.

History can contain arguments, URLs, and pasted secrets. Avoid putting credentials directly on command lines, and clear compromised credentials rather than relying only on history deletion.

Aliases

Create a shortcut:

alias ll='ls -la'

List aliases:

alias

Aliases are best for short interactive conveniences. Use a shell function when you need arguments or multiple steps:

mkcd() {
  mkdir -p -- "$1" && cd -- "$1"
}

Put aliases and interactive functions in ~/.bashrc.

Bash startup files

An interactive login Bash reads /etc/profile, then the first readable file from:

~/.bash_profile
~/.bash_login
~/.profile

An interactive non-login Bash reads ~/.bashrc.

A common setup makes ~/.bash_profile load ~/.bashrc:

if [[ -f ~/.bashrc ]]; then
  source ~/.bashrc
fi

System-wide startup files vary by operating system and packaging. The Bash startup-file documentation describes Bash’s exact rules.

Write a Bash script

Create backup.sh:

#!/usr/bin/env bash

set -o errexit
set -o nounset
set -o pipefail

source_dir=${1:?Usage: backup.sh DIRECTORY}

tar -czf backup.tar.gz -- "$source_dir"

Make it executable and run it:

chmod +x backup.sh
./backup.sh "$HOME/Documents"

The shebang selects Bash from the current PATH. This is convenient for user-managed environments. For system scripts that require a known interpreter path, use the path appropriate to that system.

The three shell options make many failures visible, but they do not replace error handling and testing.

Check syntax without running the script:

bash -n backup.sh

Bash scripts are not automatically portable sh

Arrays, [[ ... ]], source, pipefail, and several expansion forms are Bash features.

If a script starts with:

#!/bin/sh

write it for the POSIX shell language and test it with the actual /bin/sh implementations you support. Replace Bash-specific constructs, for example:

if [ -f "$file" ]; then
  . "$file"
fi

Do not assume /bin/sh is Bash. On some systems it is another shell.

The current POSIX Shell Command Language defines the portable baseline.

Use the shell your system provides

To see your configured login shell:

printf '%s\n' "$SHELL"

To see approved login shells on Unix-like systems:

cat /etc/shells

Changing the default shell is an account-level choice. Follow your operating system’s instructions and use a path listed in /etc/shells; do not copy a chsh command without checking where that shell is installed.

Tagged: CLI · All topics
~~~

Related posts about cli: