D

Bash

Bash scripting reference — variables, conditions, loops, arrays and string operations.

Updated 2026-09-03

On this page

Variables & Arguments

NAME="value"          # no spaces around =
echo "$NAME"           # always quote expansions
readonly PI=3.14159    # constant
 
echo "$1"               # first positional argument
echo "$#"               # argument count
echo "$@"               # all arguments, each as a separate word
echo "$0"               # script name

Always quote "$var"

An unquoted $var is subject to word splitting and globbing — a value with a space or a * in it will silently break your script. Quote every expansion unless you specifically need splitting.

Conditions

#!/bin/bash
 
if [[ -f "$1" ]]; then
    echo "File exists"
elif [[ -d "$1" ]]; then
    echo "It's a directory"
else
    echo "File not found"
fi
[[ -f FILE ]]

True if FILE exists and is a regular file.

[[ -d DIR ]]

True if DIR exists and is a directory.

[[ -z "$VAR" ]]

True if VAR is empty or unset.

[[ "$A" == "$B" ]]

String equality. Use -eq/-ne/-lt/-gt for numeric comparison instead.

[[ ]] vs. [ ]

Prefer [[ ... ]] (a Bash keyword) over [ ... ] (the POSIX test command) in Bash scripts — it doesn't word-split unquoted variables, supports &&/|| directly inside it, and gives clearer syntax errors.

Loops

for f in *.log; do
    echo "Processing $f"
done
 
for i in {1..5}; do
    echo "$i"
done
 
while read -r line; do
    echo "$line"
done < input.txt
 
count=0
until [[ $count -ge 3 ]]; do
    echo "$count"
    ((count++))
done

Functions

deploy() {
    local env="$1"
    local version="${2:-latest}"
 
    echo "Deploying $version to $env"
    return 0
}
 
deploy staging 1.4.2
echo "Exit code: $?"

local scoping

Declare function-internal variables with local — without it, every variable is global, and a helper function can silently clobber a variable the caller was relying on.

Arrays

services=("api" "worker" "scheduler")
 
echo "${services[0]}"        # first element
echo "${services[@]}"        # every element
echo "${#services[@]}"       # array length
 
for svc in "${services[@]}"; do
    echo "Restarting $svc"
done
 
declare -A ports=([api]=8080 [worker]=9090)
echo "${ports[api]}"

String Operations

str="Hello-World"
 
echo "${str,,}"          # lowercase: hello-world
echo "${str^^}"          # uppercase: HELLO-WORLD
echo "${str/World/Bash}" # replace: Hello-Bash
echo "${str:0:5}"        # substring: Hello
echo "${#str}"            # length: 11
echo "${str%%-*}"        # remove longest match from the end: Hello

Pipes & Redirection

command1 | command2

Pipes command1's stdout into command2's stdin.

command > out.txt

Redirects stdout to a file, overwriting it.

command >> out.txt

Redirects stdout to a file, appending.

command 2> err.txt

Redirects stderr only, leaving stdout on the terminal.

command > out.txt 2>&1

Redirects stdout to a file, then redirects stderr to wherever stdout now points — combines both into the same file.

command < input.txt

Feeds a file's contents to a command's stdin.

Exit Codes

some_command
if [[ $? -ne 0 ]]; then
    echo "Command failed" >&2
    exit 1
fi
 
# equivalent, and more idiomatic:
if ! some_command; then
    echo "Command failed" >&2
    exit 1
fi

0 means success, everything else is failure

By convention, exit code 0 means success and any non-zero code means failure — the specific non-zero value is program-defined (127 means "command not found," 126 means "found but not executable," 130 means killed by Ctrl-C/SIGINT).

Debugging

bash -x script.sh

Runs a script printing every command after expansion, before execution — the fastest way to see what a script is actually doing versus what you think it's doing.

set -euo pipefail

Standard safety header for scripts: -e exits on any unhandled error, -u errors on unset variables, -o pipefail makes a pipeline fail if any stage fails, not just the last one.

Put set -euo pipefail at the top of every script

Without it, a failed command in the middle of a script is silently ignored and the script keeps running against bad state — one of the most common sources of scripts that "worked in testing" and corrupted something in production.

Official documentation