---
title: "Bash"
description: "Bash is a Unix shell and command language written by Brian Fox for the GNU Project as a free software replacement for the Bourne shell."
author: "Mohammad Abu Mattar"
canonical: https://mkabumattar.com/cheatsheets/bash
---

# Bash

Bash is a Unix shell and command language written by Brian Fox for the GNU Project as a free software replacement for the Bourne shell.

Browse the sections below to explore Bash commands, syntax, and examples.

## Getting Started

Fundamental Bash concepts and syntax for beginners.

### Simple Script

Illustrates a basic Bash script structure and execution.

**Keywords:** script, bash, shebang, echo, variable, execute

#### Basic script with variable

```bash
#!/bin/bash

VAR="world"
echo "Hello $VAR!"
```

_exec_
```bash
bash ./script.sh
```

_output_
```bash
Hello world!
```

A basic Bash script that declares a variable and prints a greeting.

- The shebang (#!/bin/bash) specifies the Bash interpreter.
- Variables are assigned without spaces around '='.

#### Script with error handling

```bash
#!/bin/bash

set -e
VAR="${1:-world}"
echo "Hello $VAR!"
```

_exec_
```bash
bash ./script.sh universe
```

_input_
```bash
universe
```

_output_
```bash
Hello universe!
```

Uses 'set -e' to exit on errors and a default variable value if no argument is provided.

- The 'set -e' ensures the script stops on any error.
- Use ${VAR:-default} for fallback values.

**Best practices:**

- Always include a shebang line (#!/bin/bash).
- Quote variables to prevent word splitting.
- Use 'set -e' for basic error handling.

**Common errors:**

- **Spaces around = in assignment**: Use VAR=value, not VAR = value.
- **Missing execute permissions**: Run 'chmod +x script.sh' before execution.

### Script Arguments

How to handle command-line arguments in Bash scripts.

**Keywords:** arguments, parameters, bash, script

#### Accessing positional arguments

```bash
#!/bin/bash
echo "Script: $0"
echo "First arg: $1"
echo "Second arg: $2"
echo "Total args: $#"
```

_exec_
```bash
bash ./args.sh hello world
```

_input_
```bash
hello world
```

_output_
```bash
Script: ./args.sh
First arg: hello
Second arg: world
Total args: 2
```

Demonstrates accessing script name ($0), positional arguments ($1, $2), and argument count ($#).

- Quote arguments with spaces to treat as single arguments.
- Use ${N} for arguments beyond $9.

#### Iterating over arguments

```bash
#!/bin/bash
for arg in "$@"; do
  echo "Argument: $arg"
done
```

_exec_
```bash
bash ./loop_args.sh "first arg" second
```

_input_
```bash
first arg second
```

_output_
```bash
Argument: first arg
Argument: second
```

Uses $@ to iterate over arguments, preserving spaces.

- Use "$@" to handle arguments with spaces correctly.

**Best practices:**

- Validate input arguments to prevent errors.
- Use "$@" for iterating over arguments.
- Provide default values with ${VAR:-default}.

**Common errors:**

- **Unquoted $* causing word splitting**: Use "$@" instead of $* for argument iteration.

**Advanced notes:**

- **Argument Processing:** Use 'shift' to process and remove arguments one by one.
- **Validation:** Check if arguments are provided using [ -z "$1" ].
- **Default Values:** Use ${VAR:-default} for fallback values.

### Functions

Defining and using functions in Bash scripts.

**Keywords:** functions, bash, script

#### Basic function definition

```bash
#!/bin/bash

function greet() {
  echo "Hello, $1!"
}

greet "world"
```

_exec_
```bash
bash ./greet.sh
```

_output_
```bash
Hello, world!
```

Defines a simple function to greet a user.

- 'function' keyword is optional in Bash.

#### Function with local variables

```bash
#!/bin/bash

function add() {
  local sum=$(( $1 + $2 ))
  echo "Sum: $sum"
}

add 5 10
```

_exec_
```bash
bash ./add.sh
```

_output_
```bash
Sum: 15
```

Demonstrates using local variables within a function.

- 'local' restricts the variable scope to the function.

#### Function with return status

```bash
#!/bin/bash

function check_even() {
  if (( $1 % 2 == 0 )); then
    return 0
  else
    return 1
  fi
}

check_even 4
if [ $? -eq 0 ]; then
  echo "Even"
else
  echo "Odd"
fi
```

_exec_
```bash
bash ./check_even.sh
```

_output_
```bash
Even
```

Checks if a number is even using return status.

- $? captures the exit status of the last command.

**Best practices:**

- Use descriptive names for functions.
- Document function purpose and parameters.

**Common errors:**

- **'command not found' when calling a function**: 'source' the script or use './script.sh'.

**Advanced notes:**

- **Function Parameters:** Use $1, $2, ... for positional parameters.
- **Return Values:** Use 'return' for exit status, not for values.
- **Local Variables:** Use 'local' to define variables within a function.

### Comments

Adding comments in Bash scripts for clarity.

**Keywords:** comments, bash, script

#### Single-line comment

```bash
# This is a single-line comment

```

Demonstrates a single-line comment in Bash.

- Use '#' for single-line comments.

#### Multi-line comment

```bash
: 'This is a
multi-line comment
'

```

':' allows for multi-line comments.

- Use ': ' for multi-line comments.

**Best practices:**

- Use comments to explain complex logic.
- Keep comments concise and relevant.

**Common errors:**

- **Comment not recognized**: Ensure comments start with '#'.
- **Multi-line comment not working**: Use ': <<' for multi-line comments.

**Advanced notes:**

- **Commenting Style:** Use '#' for single-line comments and ': <<' for multi-line comments.
- **Documentation:** Consider using 'docstring' style for function documentation.

### Variables

Understanding and using variables in Bash scripts.

**Keywords:** variables, bash, script

#### Declaring a variable

```bash
#!/bin/bash

VAR="Hello"
echo "$VAR"
```

_exec_
```bash
bash ./var.sh
```

_output_
```bash
Hello
```

Declares a variable and prints its value.

- Use double quotes to prevent word splitting.

**Best practices:**

- Use uppercase for variable names.
- Quote variables to prevent word splitting.

**Common errors:**

- **'command not found' when using a variable**: 'source' the script or use './script.sh'.

**Advanced notes:**

- **Variable Scope:** $VAR is global, local VAR is local to the function.
- **'declare' command:** 'declare -i' for integer variables.

### Conditionals

Using if-else statements in Bash scripts.

**Keywords:** conditionals, if, else, bash, script

#### Basic if statement

```bash
#!/bin/bash

if [[ -z "$string" ]]; then
  echo "String is empty"
elif [[ -n "$string" ]]; then
  echo "String is not empty"
fi
```

Checks if a string is empty or not using if-else statements.

- Use [[ ]] for conditional expressions.
- Use -z to check if a string is empty.

**Best practices:**

- Use spaces around brackets in conditions.
- Quote variables to prevent word splitting.

**Common errors:**

- **Missing spaces around brackets**: Use [ condition ] instead of [condition].
- **'command not found' when using if statement**: 'source' the script or use './script.sh'.

**Advanced notes:**

- **Nested if statements:** You can nest if statements for complex conditions.
- **Using 'elif':** 'elif' allows for multiple conditions.
- **Using 'case':** 'case' is an alternative to multiple if-else statements.

### Shell Execution

Executing shell commands from within a Bash script.

**Keywords:** execution, shell, bash, script

#### Executing a command

```bash
#!/bin/bash

ls -l
```

_exec_
```bash
bash ./exec.sh
```

_output_
```bash
total 0
-rw-r--r-- 1 user group 0 Mar 15 12:00 file.txt
```

Executes the 'ls -l' command to list files in long format.

- Use backticks or $() for command substitution.

**Best practices:**

- Use '$(command)' for command substitution.
- Quote variables to prevent word splitting.

**Common errors:**

- **'command not found' when executing a command**: 'source' the script or use './script.sh'.
- **Command substitution not working**: Use '$(command)' instead of `command`.

**Advanced notes:**

- **Command substitution:** Use $(command) instead of `command` for better readability.
- **Pipelines:** Use '|' to pipe output from one command to another.
- **Redirection:** Use '>' to redirect output to a file.

## Loops

Iterating over data and controlling loop flow in Bash.

### For Loops

Iterating over lists, ranges, and using C-style for loops.

**Keywords:** for, loop, iterate, range, bash

#### Basic for loop over files

```bash
#!/bin/bash

for i in /etc/rc.*; do
  echo "$i"
done
```

_exec_
```bash
bash ./for_files.sh
```

_output_
```bash
/etc/rc.common
/etc/rc.local
```

Iterates over all files matching the glob pattern /etc/rc.* and prints each one.

- Glob patterns are expanded by the shell before the loop runs.
- Always quote variables inside loops to handle spaces in filenames.

#### C-like for loop

```bash
#!/bin/bash

for ((i = 0; i < 5; i++)); do
  echo "Index: $i"
done
```

_exec_
```bash
bash ./c_for.sh
```

_output_
```bash
Index: 0
Index: 1
Index: 2
Index: 3
Index: 4
```

Uses C-style syntax with double parentheses for arithmetic-based loops.

- Double parentheses (( )) allow arithmetic expressions.
- Variables inside (( )) do not need the $ prefix.

#### Range loop

```bash
#!/bin/bash

for i in {1..5}; do
  echo "Number: $i"
done
```

_exec_
```bash
bash ./range.sh
```

_output_
```bash
Number: 1
Number: 2
Number: 3
Number: 4
Number: 5
```

Uses brace expansion to generate a sequence from 1 to 5.

- Brace expansion does not work with variables; use seq or C-style loops instead.

#### Range loop with step size

```bash
#!/bin/bash

for i in {5..50..5}; do
  echo "$i"
done
```

_exec_
```bash
bash ./range_step.sh
```

_output_
```bash
5
10
15
20
25
30
35
40
45
50
```

Generates a range from 5 to 50 with a step size of 5.

- The syntax is {start..end..step}.
- Step size requires Bash 4.0 or newer.

**Best practices:**

- Use double quotes around variables inside loops to handle spaces.
- Prefer C-style loops for numeric iteration with variables.
- Use seq as a fallback when brace expansion with variables is needed.

**Common errors:**

- **Brace expansion not working with variables**: Use C-style for loop or seq: for i in $(seq 1 $n); do
- **Missing 'do' or 'done' keywords**: Ensure every 'for' has a matching 'do' and 'done'.

### While Loops

Repeating commands while a condition is true.

**Keywords:** while, loop, read, infinite, bash

#### Basic while loop

```bash
#!/bin/bash

count=0
while [[ $count -lt 5 ]]; do
  echo "Count: $count"
  ((count++))
done
```

_exec_
```bash
bash ./while.sh
```

_output_
```bash
Count: 0
Count: 1
Count: 2
Count: 3
Count: 4
```

Loops while count is less than 5, incrementing each iteration.

- Use [[ ]] for conditional tests in while loops.
- (( )) is used for arithmetic increment.

#### Reading lines from a file

```bash
#!/bin/bash

while read -r line; do
  echo "Line: $line"
done < file.txt
```

_exec_
```bash
bash ./read_lines.sh
```

_output_
```bash
Line: first line
Line: second line
```

Reads a file line by line using input redirection.

- The -r flag prevents backslash interpretation.
- Input redirection < feeds the file into the while loop.

#### Infinite loop

```bash
#!/bin/bash

while true; do
  echo "Press Ctrl+C to stop"
  sleep 1
done
```

_exec_
```bash
bash ./infinite.sh
```

_output_
```bash
Press Ctrl+C to stop
Press Ctrl+C to stop
```

Runs indefinitely until interrupted with Ctrl+C.

- Use 'break' inside the loop to exit based on a condition.
- Always include a sleep or wait to prevent CPU overuse.

**Best practices:**

- Always use 'read -r' to prevent backslash mangling.
- Include a termination condition to avoid infinite loops.
- Use 'sleep' in infinite loops to reduce CPU usage.

**Common errors:**

- **Loop never terminates**: Ensure the loop condition changes or use 'break'.
- **Variables modified inside loop not visible outside**: Avoid piping into while; use redirection instead.

### Loop Control

Controlling loop execution with break, continue, and select.

**Keywords:** break, continue, select, loop, control, bash

#### Using break and continue

```bash
#!/bin/bash

for i in {1..10}; do
  if [[ $i -eq 5 ]]; then
    continue
  fi
  if [[ $i -eq 8 ]]; then
    break
  fi
  echo "Number: $i"
done
```

_exec_
```bash
bash ./control.sh
```

_output_
```bash
Number: 1
Number: 2
Number: 3
Number: 4
Number: 6
Number: 7
```

Skips 5 with continue and exits the loop at 8 with break.

- continue skips to the next iteration.
- break exits the loop entirely.

#### Select menu

```bash
#!/bin/bash

select option in "Start" "Stop" "Quit"; do
  case $option in
    "Start") echo "Starting..." ;;
    "Stop") echo "Stopping..." ;;
    "Quit") break ;;
    *) echo "Invalid option" ;;
  esac
done
```

_exec_
```bash
bash ./select.sh
```

_input_
```bash
1
```

_output_
```bash
1) Start
2) Stop
3) Quit
#? Starting...
```

Presents a numbered menu and executes the corresponding action.

- select automatically generates a numbered menu.
- Use break to exit the select loop.

**Best practices:**

- Use break to exit loops cleanly.
- Use continue to skip iterations rather than nested if blocks.
- Combine select with case for interactive menus.

**Common errors:**

- **break or continue used outside a loop**: Ensure break and continue are inside a for, while, or until loop.
- **Select menu does not exit**: Add a break statement inside the quit option.

## Arrays

Working with indexed arrays in Bash.

### Defining Arrays

Creating and initializing indexed arrays in Bash.

**Keywords:** array, define, declare, bash

#### Defining an array

```bash
#!/bin/bash

Fruits=('Apple' 'Banana' 'Orange')
echo "${Fruits[0]}"
echo "${Fruits[1]}"
echo "${Fruits[2]}"
```

_exec_
```bash
bash ./array.sh
```

_output_
```bash
Apple
Banana
Orange
```

Defines an indexed array with three elements and prints each one.

- Array indices start at 0 in Bash.
- Elements are separated by spaces inside parentheses.

#### Index assignment

```bash
#!/bin/bash

Fruits[0]="Apple"
Fruits[1]="Banana"
Fruits[5]="Orange"
echo "${Fruits[@]}"
```

_exec_
```bash
bash ./index_assign.sh
```

_output_
```bash
Apple Banana Orange
```

Assigns values to specific indices; indices do not need to be contiguous.

- Bash arrays are sparse; you can skip indices.
- Use ${array[@]} to print all elements.

**Best practices:**

- Use parentheses syntax for initializing arrays with multiple elements.
- Quote array expansions to preserve elements with spaces.
- Use 'declare -a' for explicit array declaration.

**Common errors:**

- **Spaces around = in array assignment**: Use Fruits=("Apple" "Banana"), not Fruits = ("Apple" "Banana").
- **Forgetting quotes around elements with spaces**: Quote each element: Fruits=("Red Apple" "Yellow Banana").

### Array Operations

Common operations on Bash indexed arrays.

**Keywords:** array, operations, push, remove, length, slice, bash

#### Accessing array elements

```bash
#!/bin/bash

Fruits=('Apple' 'Banana' 'Orange' 'Mango' 'Kiwi')
echo "First: ${Fruits[0]}"
echo "Last: ${Fruits[-1]}"
echo "All: ${Fruits[@]}"
echo "Count: ${#Fruits[@]}"
echo "Slice: ${Fruits[@]:1:3}"
echo "Keys: ${!Fruits[@]}"
```

_exec_
```bash
bash ./array_ops.sh
```

_output_
```bash
First: Apple
Last: Kiwi
All: Apple Banana Orange Mango Kiwi
Count: 5
Slice: Banana Orange Mango
Keys: 0 1 2 3 4
```

Demonstrates accessing first, last, all elements, count, slice, and keys of an array.

- Negative indices like ${Fruits[-1]} require Bash 4.2+.
- ${Fruits[@]:offset:length} extracts a slice of the array.
- ${!Fruits[@]} returns all valid indices.

#### Push, remove, and concatenate

```bash
#!/bin/bash

Fruits=('Apple' 'Banana' 'Orange')

# Push
Fruits+=('Mango')
echo "After push: ${Fruits[@]}"

# Remove by regex
Fruits=("${Fruits[@]/Ban*/}")
echo "After remove: ${Fruits[@]}"

# Unset by index
unset 'Fruits[2]'
echo "After unset: ${Fruits[@]}"

# Concatenate
Veggies=('Carrot' 'Pea')
Combined=("${Fruits[@]}" "${Veggies[@]}")
echo "Combined: ${Combined[@]}"
```

_exec_
```bash
bash ./array_modify.sh
```

_output_
```bash
After push: Apple Banana Orange Mango
After remove: Apple  Orange Mango
After unset: Apple  Mango
Combined: Apple  Mango Carrot Pea
```

Shows how to append, remove by pattern, unset by index, and merge arrays.

- The += operator appends elements to an existing array.
- Pattern removal may leave empty elements in the array.
- unset removes the element but does not reindex the array.

**Best practices:**

- Use += to append elements to arrays.
- Use unset to remove specific elements by index.
- Quote "${array[@]}" to preserve elements with spaces.

**Common errors:**

- **Array not reindexed after unset**: Reassign the array: array=("${array[@]}") to reindex.
- **Pattern removal leaving empty strings**: Filter empty elements after removal.

### Array Iteration

Iterating over array elements in Bash.

**Keywords:** array, iterate, loop, for, bash

#### Iterating over array elements

```bash
#!/bin/bash

Fruits=('Apple' 'Banana' 'Orange')

for fruit in "${Fruits[@]}"; do
  echo "Fruit: $fruit"
done
```

_exec_
```bash
bash ./iterate.sh
```

_output_
```bash
Fruit: Apple
Fruit: Banana
Fruit: Orange
```

Loops over each element in the array using "${array[@]}".

- Always quote "${array[@]}" to handle elements with spaces.
- Use "${!array[@]}" to iterate over indices instead.

**Best practices:**

- Always double-quote "${array[@]}" in for loops.
- Use index-based iteration when you need the position.
- Avoid unquoted ${array[*]} as it joins on IFS.

**Common errors:**

- **Elements with spaces split into separate items**: Use "${Fruits[@]}" instead of ${Fruits[@]}.

## Dictionaries

Working with associative arrays (key-value pairs) in Bash.

### Associative Arrays

Declaring and using associative arrays in Bash 4+.

**Keywords:** associative, array, dictionary, declare, bash

#### Declaring and using an associative array

```bash
#!/bin/bash

declare -A sounds
sounds[dog]="bark"
sounds[cat]="meow"
sounds[bird]="tweet"

echo "Dog: ${sounds[dog]}"
echo "All values: ${sounds[@]}"
echo "All keys: ${!sounds[@]}"
echo "Count: ${#sounds[@]}"
```

_exec_
```bash
bash ./assoc.sh
```

_output_
```bash
Dog: bark
All values: tweet bark meow
All keys: bird dog cat
Count: 3
```

Creates an associative array of animal sounds and demonstrates access patterns.

- declare -A is required for associative arrays.
- The order of keys is not guaranteed.
- Requires Bash 4.0 or newer.

#### Deleting a key

```bash
#!/bin/bash

declare -A sounds
sounds[dog]="bark"
sounds[cat]="meow"
unset 'sounds[dog]'
echo "Remaining keys: ${!sounds[@]}"
echo "Remaining values: ${sounds[@]}"
```

_exec_
```bash
bash ./assoc_delete.sh
```

_output_
```bash
Remaining keys: cat
Remaining values: meow
```

Removes a key from the associative array using unset.

- Quote the key in unset to prevent glob expansion.

**Best practices:**

- Always use 'declare -A' before using an associative array.
- Quote keys containing special characters.
- Check if a key exists before accessing it.

**Common errors:**

- **Associative array behaves like indexed array**: Ensure you use declare -A; without it, Bash treats it as indexed.
- **'declare: -A: invalid option'**: Upgrade to Bash 4.0+ which supports associative arrays.

### Dictionary Iteration

Iterating over keys and values in associative arrays.

**Keywords:** associative, iterate, keys, values, bash

#### Iterating over values and keys

```bash
#!/bin/bash

declare -A sounds
sounds[dog]="bark"
sounds[cat]="meow"
sounds[bird]="tweet"

echo "=== Values ==="
for val in "${sounds[@]}"; do
  echo "$val"
done

echo "=== Keys ==="
for key in "${!sounds[@]}"; do
  echo "$key: ${sounds[$key]}"
done
```

_exec_
```bash
bash ./dict_iter.sh
```

_output_
```bash
=== Values ===
tweet
bark
meow
=== Keys ===
bird: tweet
dog: bark
cat: meow
```

Iterates over all values with ${sounds[@]} and all keys with ${!sounds[@]}.

- The iteration order is not guaranteed for associative arrays.
- Use "${!array[@]}" to get keys and access values via ${array[$key]}.

**Best practices:**

- Iterate over keys when you need both key and value.
- Always quote array expansions in for loops.
- Check array length before iterating.

**Common errors:**

- **Iterating over values but needing keys**: Use "${!sounds[@]}" to iterate over keys.

## Parameter Expansions

Bash string manipulation and parameter expansion techniques.

### String Substitution

Replacing and removing substrings using parameter expansion.

**Keywords:** substitution, replace, prefix, suffix, parameter, expansion, bash

#### Replacing and removing substrings

```bash
#!/bin/bash

name="John Smith"
echo "${name/J/j}"
echo "${name//o/0}"

file="hello.tar.gz"
echo "${file%.*}"
echo "${file%%.*}"
echo "${file#*.}"
echo "${file##*.}"
```

_exec_
```bash
bash ./substitution.sh
```

_output_
```bash
john Smith
J0hn Smith
hello.tar
hello
tar.gz
gz
```

Demonstrates single and global replacement, and prefix and suffix removal with parameter expansion.

- ${var/pattern/replacement} replaces the first match.
- ${var//pattern/replacement} replaces all matches.
- ${var%pattern} removes the shortest suffix match.
- ${var%%pattern} removes the longest suffix match.
- ${var#pattern} removes the shortest prefix match.
- ${var##pattern} removes the longest prefix match.

**Best practices:**

- Use %% and
- Prefer parameter expansion over external tools like sed for simple replacements.
- Use # for prefix removal and % for suffix removal.

**Common errors:**

- **Confusing # and % directions**: Remember that # removes from the left (prefix) and % removes from the right (suffix).
- **Pattern not matching as expected**: Use * as a wildcard in patterns for flexible matching.

### String Slicing

Extracting substrings and getting string length.

**Keywords:** substring, slice, length, offset, bash

#### Substrings and length

```bash
#!/bin/bash

name="Hello World"

echo "${name:0:5}"
echo "${name:6}"
echo "${name:(-5)}"
echo "${#name}"
```

_exec_
```bash
bash ./slice.sh
```

_output_
```bash
Hello
World
World
11
```

Extracts substrings by offset and length, and gets string length.

- ${name:offset:length} extracts a substring.
- ${name:(-N)} extracts from the end (parentheses required).
- ${#name} returns the string length.

**Best practices:**

- Use parentheses for negative offsets to avoid ambiguity with default values.
- Use ${#var} instead of external commands for string length.
- Combine slicing with conditionals for input validation.

**Common errors:**

- **Negative offset without parentheses**: Use ${name:(-1)} not ${name:-1} which is default value syntax.

### Default Values

Setting default values for unset or empty variables.

**Keywords:** default, fallback, unset, parameter, expansion, bash

#### Default value expansions

```bash
#!/bin/bash

unset foo
echo "${foo:-default_val}"
echo "foo is: '${foo}'"

echo "${foo:=assigned_val}"
echo "foo is: '${foo}'"

echo "${foo:+replacement}"

unset bar
echo "${bar:?'bar is required'}" 2>&1 || true
```

_exec_
```bash
bash ./defaults.sh
```

_output_
```bash
default_val
foo is: ''
assigned_val
foo is: 'assigned_val'
replacement
bash: bar: bar is required
```

Shows the four default value operators: use default, assign default, use alternative, and error if unset.

- ${foo:-val} uses val if foo is unset or empty, without assigning.
- ${foo:=val} assigns val to foo if unset or empty.
- ${foo:+val} uses val only if foo IS set and non-empty.
- ${foo:?message} prints error and exits if foo is unset or empty.

**Best practices:**

- Use ${foo:-val} for safe defaults without modifying the variable.
- Use ${foo:=val} when you want to persist the default assignment.
- Use ${foo:?msg} for required variables in scripts.

**Common errors:**

- **Using := in a readonly or special variable context**: Use :- instead of := for variables you do not want to modify.
- **Script exiting unexpectedly**: Check for :? expansions on unset variables; they cause immediate exit.

### Case Manipulation

Changing string case using parameter expansion.

**Keywords:** lowercase, uppercase, case, parameter, expansion, bash

#### Case conversion

```bash
#!/bin/bash

str="Hello World"

echo "${str,,}"
echo "${str^^}"
echo "${str,}"
echo "${str^}"
```

_exec_
```bash
bash ./case_manip.sh
```

_output_
```bash
hello world
HELLO WORLD
hello World
Hello World
```

Converts strings to lowercase, uppercase, and toggles the first character.

- ${str,,} converts the entire string to lowercase.
- ${str^^} converts the entire string to uppercase.
- ${str,} lowercases only the first character.
- ${str^} uppercases only the first character.
- Requires Bash 4.0 or newer.

**Best practices:**

- Use case manipulation instead of tr or awk for simple conversions.
- Remember these operators require Bash 4.0+.
- Combine with conditionals for case-insensitive comparisons.

**Common errors:**

- **Syntax error with ,, or ^^ operators**: Ensure you are running Bash 4.0+; older versions do not support this.

## I/O and Redirection

Input/output redirection, heredocs, and process substitution in Bash.

### Redirection

Redirecting standard input, output, and error streams.

**Keywords:** redirect, stdout, stderr, stdin, file, bash

#### Output and error redirection

```bash
#!/bin/bash

# Redirect stdout to file
echo "hello" > output.txt

# Append stdout to file
echo "world" >> output.txt

# Redirect stderr to file
ls /nonexistent 2> error.log

# Redirect stderr to stdout
ls /nonexistent 2>&1

# Redirect both stdout and stderr to file
ls /nonexistent &> all.log

# Discard output
ls /nonexistent 2>/dev/null

# Read stdin from file
cat < output.txt
```

_exec_
```bash
bash ./redirect.sh
```

_output_
```bash
hello
world
```

Demonstrates common redirection operators for stdout, stderr, and stdin.

- > overwrites the file; >> appends to it.
- 2> redirects stderr only.
- 2>&1 sends stderr to the same destination as stdout.
- &> redirects both stdout and stderr.
- < reads stdin from a file.

**Best practices:**

- Use >> to append and avoid overwriting important files.
- Redirect stderr to /dev/null to suppress error messages.
- Use &> when you want to capture all output.

**Common errors:**

- **File overwritten unexpectedly**: Use >> to append instead of > which overwrites.
- **Error messages still showing on screen**: Redirect stderr with 2> or 2>/dev/null.

### Heredoc and Herestring

Using heredocs and herestrings for multi-line and inline input.

**Keywords:** heredoc, herestring, multiline, input, bash

#### Heredoc

```bash
#!/bin/bash

cat <<END
Hello
World
END
```

_exec_
```bash
bash ./heredoc.sh
```

_output_
```bash
Hello
World
```

Passes multi-line text to cat using a heredoc delimiter.

- The delimiter (END) must appear alone on its own line.
- Variables inside heredocs are expanded by default.
- Use <<'END' (quoted) to prevent variable expansion.

#### Herestring

```bash
#!/bin/bash

read -r first second <<< "Hello World"
echo "First: $first"
echo "Second: $second"
```

_exec_
```bash
bash ./herestring.sh
```

_output_
```bash
First: Hello
Second: World
```

Feeds a string directly to a command using the <<< operator.

- Herestrings pass a single string as stdin.
- Useful as an alternative to echo "string" | command.

**Best practices:**

- Use quoted delimiters (<<'END') when you do not want variable expansion.
- Use herestrings instead of echo piping for simple cases.
- Indent heredoc content with <<- and tabs for readability.

**Common errors:**

- **Heredoc delimiter not recognized**: Ensure the closing delimiter has no leading spaces (use <<- with tabs).
- **Variables expanding unexpectedly in heredoc**: Quote the delimiter: <<'END' to prevent expansion.

### Reading Input

Reading user input from the terminal.

**Keywords:** read, input, prompt, interactive, bash

#### Reading user input

```bash
#!/bin/bash

read -r -p "Enter your name: " name
echo "Hello, $name!"

read -r -n 1 -p "Continue? (y/n) " answer
echo ""
echo "You chose: $answer"
```

_exec_
```bash
bash ./read_input.sh
```

_input_
```bash
Alice
y
```

_output_
```bash
Enter your name: Hello, Alice!
Continue? (y/n) You chose: y
```

Reads a full line and a single character from user input.

- -r prevents backslash interpretation.
- -p sets a prompt string.
- -n 1 reads only one character.

**Best practices:**

- Always use -r flag with read to prevent backslash mangling.
- Provide clear prompts with -p for user-friendly scripts.
- Validate input after reading.

**Common errors:**

- **Backslashes disappearing from input**: Use read -r to preserve backslashes.
- **Input not captured correctly**: Ensure the variable name is provided after read flags.

### Process Substitution

Using process substitution to treat command output as files.

**Keywords:** process, substitution, diff, bash

#### Comparing two command outputs

```bash
#!/bin/bash

diff <(ls /usr/bin) <(ls /usr/local/bin)
```

_exec_
```bash
bash ./procsub.sh
```

_output_
```bash
< file_only_in_bin
> file_only_in_local_bin
```

Uses process substitution to compare directory listings without temporary files.

- <(command) provides command output as a file descriptor.
- >(command) sends input to a command as a file descriptor.
- This avoids the need for temporary files.

**Best practices:**

- Use process substitution to avoid temporary files.
- Combine with diff, comm, or paste for comparing outputs.
- Remember that process substitution creates a subshell.

**Common errors:**

- **Syntax error near unexpected token <(**: Ensure you are using Bash, not sh; process substitution is a Bash feature.
- **Process substitution not working in scripts**: Use #!/bin/bash, not #!/bin/sh in the shebang line.

## Advanced Bash

Advanced Bash techniques for robust scripting.

### Strict Mode

Using strict mode options for safer Bash scripts.

**Keywords:** strict, set, pipefail, errexit, nounset, bash

#### Enabling strict mode

```bash
#!/bin/bash

set -euo pipefail
IFS=$'\n\t'

echo "Running in strict mode"
echo "Unset var: $UNSET_VAR"
```

_exec_
```bash
bash ./strict.sh
```

_output_
```bash
Running in strict mode
bash: UNSET_VAR: unbound variable
```

Enables strict mode that exits on errors, unset variables, and pipe failures.

- -e exits on any command failure.
- -u treats unset variables as errors.
- -o pipefail returns the exit code of the first failing command in a pipe.
- IFS=$'\n\t' prevents word splitting on spaces.

**Best practices:**

- Add 'set -euo pipefail' at the top of every script.
- Set IFS=$'\n\t' to avoid unexpected word splitting.
- Use || true for commands that are allowed to fail.

**Common errors:**

- **Script exits unexpectedly with set -e**: Use || true for commands that may fail intentionally.
- **Unbound variable error**: Use ${VAR:-default} to provide defaults for optional variables.

### Trap and Error Handling

Using trap for error handling and cleanup.

**Keywords:** trap, error, cleanup, exit, signal, bash

#### Trap on error

```bash
#!/bin/bash

trap 'echo "Error at line $LINENO"' ERR

echo "Before error"
false
echo "This will not run"
```

_exec_
```bash
bash ./trap_err.sh
```

_output_
```bash
Before error
Error at line 6
```

Traps ERR signal and prints the line number where the error occurred.

- ERR trap fires whenever a command returns a non-zero exit status.
- $LINENO contains the current line number.

#### Trap for cleanup on exit

```bash
#!/bin/bash

tmpfile=$(mktemp)
cleanup() {
  rm -f "$tmpfile"
  echo "Cleaned up $tmpfile"
}
trap cleanup EXIT

echo "Working with $tmpfile"
echo "data" > "$tmpfile"
```

_exec_
```bash
bash ./trap_exit.sh
```

_output_
```bash
Working with /tmp/tmp.XXXXXXXXXX
Cleaned up /tmp/tmp.XXXXXXXXXX
```

Uses EXIT trap to ensure temporary files are cleaned up when the script exits.

- EXIT trap runs when the script finishes, regardless of how it exits.
- Use trap for cleanup of temporary files, lock files, etc.

**Best practices:**

- Use trap EXIT for cleanup tasks.
- Use trap ERR for error logging and debugging.
- Define cleanup functions for complex teardown logic.

**Common errors:**

- **Trap not firing as expected**: Ensure the signal name is correct (ERR, EXIT, INT, TERM).
- **Cleanup function not defined when trap fires**: Define the function before the trap statement.

### Case / Switch

Pattern matching with case/esac statements.

**Keywords:** case, esac, switch, pattern, matching, bash

#### Case statement with patterns

```bash
#!/bin/bash

read -r -p "Enter a fruit: " fruit
case "$fruit" in
  "apple")
    echo "Apple is red"
    ;;
  "banana"|"plantain")
    echo "Banana is yellow"
    ;;
  "orange")
    echo "Orange is orange"
    ;;
  *)
    echo "Unknown fruit: $fruit"
    ;;
esac
```

_exec_
```bash
bash ./case_switch.sh
```

_input_
```bash
banana
```

_output_
```bash
Enter a fruit: Banana is yellow
```

Matches user input against patterns and executes the corresponding block.

- Use | to match multiple patterns in one branch.
- *) acts as the default/fallback case.
- Each branch ends with ;; to stop matching.

**Best practices:**

- Always include a *) default case.
- Quote the variable in the case statement.
- Use | for multiple patterns in a single branch.

**Common errors:**

- **Missing ;; at end of case branch**: Every case branch must end with ;; (or ;& for fall-through).
- **Patterns not matching**: Use glob patterns (* and ?) for flexible matching.

### Printf Formatting

Formatted output with printf.

**Keywords:** printf, format, output, string, bash

#### Printf examples

```bash
#!/bin/bash

printf "Hello %s\n" "World"
printf "Number: %d\n" 42
printf "Float: %.2f\n" 3.14159
printf "Padded: %10s|\n" "right"
printf "Padded: %-10s|\n" "left"
printf '%s\n' "line1" "line2" "line3"
```

_exec_
```bash
bash ./printf.sh
```

_output_
```bash
Hello World
Number: 42
Float: 3.14
Padded:      right|
Padded: left      |
line1
line2
line3
```

Demonstrates printf with string, integer, float, and padding format specifiers.

- %s for strings, %d for integers, %f for floats.
- printf does not add a newline automatically; use \n.
- printf reuses the format string for extra arguments.

**Best practices:**

- Use printf instead of echo for portable and predictable output.
- Always include \n for newlines explicitly.
- Use printf for formatted tables and aligned output.

**Common errors:**

- **Missing newline in printf output**: Add \n to the format string: printf "text\n".
- **Wrong format specifier causing errors**: Match specifiers to argument types: %s for strings, %d for numbers.

### Special Variables

Built-in special variables in Bash.

**Keywords:** special, variables, exit, status, pid, bash

#### Common special variables

```bash
#!/bin/bash

echo "Exit status of last command: $?"
echo "PID of current script: $$"
echo "Script name: $0"
echo "Last argument of previous command: $_"
echo "Random number: $RANDOM"
echo "Current line number: $LINENO"

sleep 0.1 &
echo "PID of last background process: $!"
wait

true | false | true
echo "PIPESTATUS: ${PIPESTATUS[@]}"
```

_exec_
```bash
bash ./special.sh
```

_output_
```bash
Exit status of last command: 0
PID of current script: 12345
Script name: ./special.sh
Last argument of previous command: ./special.sh
Random number: 28317
Current line number: 7
PID of last background process: 12346
PIPESTATUS: 0 1 0
```

Displays the most commonly used Bash special variables.

- $? is the exit status of the last command (0 = success).
- $$ is the PID of the current shell.
- $! is the PID of the last background process.
- ${PIPESTATUS[@]} gives exit codes of all commands in the last pipeline.
- $RANDOM generates a random integer between 0 and 32767.

**Best practices:**

- Check $? immediately after the command you want to inspect.
- Use ${PIPESTATUS[@]} with set -o pipefail for pipeline error handling.
- Use $$ for creating unique temporary filenames.

**Common errors:**

- **$? gives unexpected value**: $? is overwritten by every command; capture it immediately.
- **$RANDOM not truly random**: $RANDOM is pseudo-random; use /dev/urandom for cryptographic needs.

### Brace Expansion

Generating arbitrary strings with brace expansion.

**Keywords:** brace, expansion, sequence, generate, bash

#### Brace expansion patterns

```bash
#!/bin/bash

echo {A,B}.js
echo {1..5}
echo {1..10..2}
echo {a..z..3}
echo pre{fix,lude,pare}
echo {1..3}{a..c}
```

_exec_
```bash
bash ./brace.sh
```

_output_
```bash
A.js B.js
1 2 3 4 5
1 3 5 7 9
a d g j m p s v y
prefix prelude prepare
1a 1b 1c 2a 2b 2c 3a 3b 3c
```

Shows comma-separated, range, stepped, and combined brace expansions.

- {A,B} expands to each comma-separated item.
- {1..5} generates a numeric sequence.
- {1..10..2} generates a sequence with a step.
- Brace expansion is performed before other expansions.

**Best practices:**

- Use brace expansion for creating multiple files or directories quickly.
- Combine brace expansions for Cartesian product generation.
- Remember that brace expansion happens before variable expansion.

**Common errors:**

- **Brace expansion not working with variables**: Brace expansion happens before variable expansion; use eval or seq instead.
- **Spaces inside braces causing issues**: Do not use spaces inside brace expressions.

### History Expansion

Reusing and modifying previous commands with history expansion.

**Keywords:** history, expansion, recall, previous, command, bash

#### History expansion operators

```bash
# Re-run the last command
!!

# Last argument of the previous command
echo !$

# All arguments of the previous command
echo !*

# Run command number 42 from history
!42

# Replace text in the last command and re-run
!!:s/from/to/
```

Demonstrates history expansion operators for recalling and modifying commands.

- !! repeats the entire last command.
- !$ is the last argument of the previous command.
- !* is all arguments of the previous command.
- !n runs command number n from history.
- !!:s/from/to/ substitutes text in the last command.
- History expansion is mainly useful in interactive shells.

**Best practices:**

- Use !! with sudo to repeat the last command as root.
- !$ is a quick way to reuse the last argument.
- Avoid history expansion in scripts; it is disabled by default.

**Common errors:**

- **History expansion not working in scripts**: History expansion is disabled in non-interactive shells by default.
- **Unexpected expansion with exclamation marks**: Use single quotes to prevent history expansion in interactive shells.
