The Bash shell
By Flavio Copes
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
Navigate the filesystem
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:
HOME: your home directoryUSER: your user name on many systemsSHELL: your configured login shellPATH: directories searched for commandsPWD: the current working directory
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.
Related posts about cli: