# The Bash shell

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

Author: [Flavio Copes](https://flaviocopes.com/about/) | Published: 2019-01-14 | Updated: 2026-07-18 | Topics: [CLI](https://flaviocopes.com/tags/cli/) | Canonical: https://flaviocopes.com/bash/

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
bash
```

Check the running Bash release:

```bash
bash --version
```

The [GNU Bash manual](https://www.gnu.org/software/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:

```bash
command -v ls
command -v node
```

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

Run a command with arguments:

```bash
ls -la ~/Documents
```

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

```bash
man ls
help cd
```

## Navigate the filesystem

Print the current directory:

```bash
pwd
```

List its contents:

```bash
ls
```

Change directory:

```bash
cd ~/Documents
```

Move to the parent directory:

```bash
cd ..
```

Return home:

```bash
cd
```

Create a directory:

```bash
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:

```bash
cd "$HOME/My Projects"
```

Single quotes preserve every character literally:

```bash
printf '%s\n' '$HOME'
```

Double quotes still allow variable and command expansion:

```bash
printf '%s\n' "$HOME"
```

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

```bash
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:

```bash
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:

```bash
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 `=`:

```bash
project_name='my app'
```

Read it:

```bash
printf '%s\n' "$project_name"
```

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

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

Frequently used environment variables include:

- `HOME`: your home directory
- `USER`: your user name on many systems
- `SHELL`: your configured login shell
- `PATH`: directories searched for commands
- `PWD`: the current working directory

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

```bash
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:

```bash
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:

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

Write standard output to a file, replacing its contents:

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

Append instead:

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

Redirect standard error:

```bash
some-command 2> errors.txt
```

Redirect both standard output and standard error in Bash:

```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:

```bash
npm install && npm test
```

Run the second only if the first fails:

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

Run commands sequentially regardless of status:

```bash
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:

```bash
long-command &
```

List jobs started by the current shell:

```bash
jobs
```

Bring job 1 to the foreground:

```bash
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:

```bash
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:

```bash
alias ll='ls -la'
```

List aliases:

```bash
alias
```

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

```bash
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:

```text
~/.bash_profile
~/.bash_login
~/.profile
```

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

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

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

System-wide startup files vary by operating system and packaging. The [Bash startup-file documentation](https://www.gnu.org/software/bash/manual/html_node/Bash-Startup-Files.html) describes Bash's exact rules.

## Write a Bash script

Create `backup.sh`:

```bash
#!/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:

```bash
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
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:

```bash
#!/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:

```sh
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](https://pubs.opengroup.org/onlinepubs/9799919799/utilities/V3_chap02.html) defines the portable baseline.

## Use the shell your system provides

To see your configured login shell:

```bash
printf '%s\n' "$SHELL"
```

To see approved login shells on Unix-like systems:

```bash
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.
