Skip to main content
In this tutorial, we’ll build a Bash script that picks a random lunch spot from a list—without repeats—by combining indexed arrays and simple file-based persistence. This pattern is perfect for one-time draws, rotating duties, or any scenario where you want to consume items until the list is exhausted. Imagine a team of 20 colleagues who vote on their favorite restaurants each Friday. Everyone adds their go-to spot to an array. The script then:
  • Reads the list into memory
  • Selects a random entry
  • Displays it
  • Removes it from both memory and disk
  • Exits with an error if the list is missing or empty

Why In-Memory vs Persistent Storage?

  1. Manipulating data in memory means changes disappear once the script ends.
The image is a slide titled "Arrays demo" that explains "manipulating data in-memory," referring to initializing or modifying values directly in variables within a script or program.
  1. Persistent storage (files, databases) retains data between runs.
The image is a diagram titled "Arrays demo" that explains the concept of a persistent storage method, which retains data after restarting an application or rebooting a machine, with icons representing databases and files.
This script uses Bash 4.0+ for the mapfile builtin.

Key Bash Features

  • mapfile -t: Loads lines from a file (or stdin) into an array.
The image is a slide titled "Arrays demo" that describes the shell built-in command "mapfile," which reads input data and stores it in an array format, available from bash 4.0 onwards.
  • $RANDOM: Yields a pseudo-random integer between 0 and 32767.
The image is a slide from an "Arrays demo" presentation, describing a special shell variable "RANDOM" that generates a random integer between 0 and 32767.

Exit Code Reference

Exit CodeCondition
150 (FILE_NOT_FOUND)food_places.txt is missing
180 (NO_OPTIONS_LEFT)No entries remain in food_places.txt

1. Prepare the Data File

Create food_places.txt with initial entries:
cat <<EOF > food_places.txt
Ramen
Sushi
Tacos
Dal makhani
EOF

2. Initialize the Script

Create lunch_selector.sh:
#!/usr/bin/env bash
declare -a lunch_options

# Determine script directory
work_dir=$(dirname "$(readlink -f "${0}")")
food_places="${work_dir}/food_places.txt"

# Exit codes
readonly FILE_NOT_FOUND=150
readonly NO_OPTIONS_LEFT=180

terminate() {
  local msg="$1"
  local code="${2:-$FILE_NOT_FOUND}"
  echo "$msg" >&2
  exit "$code"
}

# Guard clause
if [[ ! -f "$food_places" ]]; then
  terminate "Error: food_places.txt file doesn't exist" "$FILE_NOT_FOUND"
fi

3. Load and Validate the Array

Populate lunch_options and ensure it’s not empty:
fillout_array() {
  mapfile -t lunch_options < "$food_places"
  if [[ ${#lunch_options[@]} -eq 0 ]]; then
    terminate "Error: No food options left. Please add options to food_places.txt" "$NO_OPTIONS_LEFT"
  fi
}

fillout_array

4. Select and Display a Random Option

Choose a random index, echo it, and remove from the in-memory list:
index=$(( RANDOM % ${#lunch_options[@]} ))
chosen="${lunch_options[$index]}"
echo "$chosen"

# Remove the selected element
unset 'lunch_options[index]'

5. Persist the Updated List

Write the remaining entries back to food_places.txt:
update_options() {
  if [[ ${#lunch_options[@]} -eq 0 ]]; then
    # Empty the file when no items remain
    : > "$food_places"
  else
    printf "%s\n" "${lunch_options[@]}" > "$food_places"
  fi
}

update_options

6. Complete Script

Here’s the full lunch_selector.sh. Don’t forget to make it executable:
#!/usr/bin/env bash
declare -a lunch_options

work_dir=$(dirname "$(readlink -f "${0}")")
food_places="${work_dir}/food_places.txt"
readonly FILE_NOT_FOUND=150
readonly NO_OPTIONS_LEFT=180

terminate() {
  local msg="$1"
  local code="${2:-$FILE_NOT_FOUND}"
  echo "$msg" >&2
  exit "$code"
}

if [[ ! -f "$food_places" ]]; then
  terminate "Error: food_places.txt file doesn't exist" "$FILE_NOT_FOUND"
fi

fillout_array() {
  mapfile -t lunch_options < "$food_places"
  if [[ ${#lunch_options[@]} -eq 0 ]]; then
    terminate "Error: No food options left. Please add options to food_places.txt" "$NO_OPTIONS_LEFT"
  fi
}

fillout_array

index=$(( RANDOM % ${#lunch_options[@]} ))
chosen="${lunch_options[$index]}"
echo "$chosen"
unset 'lunch_options[index]'

update_options() {
  if [[ ${#lunch_options[@]} -eq 0 ]]; then
    : > "$food_places"
  else
    printf "%s\n" "${lunch_options[@]}" > "$food_places"
  fi
}

update_options
Make it executable and run:
chmod +x lunch_selector.sh
./lunch_selector.sh
Each run prints a random, non-repeating lunch spot until the list is empty.

References