Skip to content

Instantly share code, notes, and snippets.

@Summertime
Last active May 29, 2025 05:05
Show Gist options
  • Save Summertime/9bd3e4bbcde3c1c0189247cef1c5fe7d to your computer and use it in GitHub Desktop.
Save Summertime/9bd3e4bbcde3c1c0189247cef1c5fe7d to your computer and use it in GitHub Desktop.
AI Horror

Custom instructions

I'm Australian

User

give me some bash boilerplate that:

  • checks bashes version and refuses to run if its older than current
  • sets up all safe and desirable shopts: globbing and lastpipe, not nullglob or whatever
  • Sets up boot, machine, and invocation id from systemd's id stuff
  • sets up variables for all the XDG dirs
  • sets up an array called "TRAP_EXIT" that will run on the exit trap

ChatGPT

Here’s a Bash script boilerplate that:

  • Verifies that the Bash version is at least a specific minimum version.
  • Enables strict/safe shell options (e.g., set -euo pipefail and useful shopt flags).
  • Pulls boot, machine, and invocation IDs from systemd.
  • Sets up XDG directory variables with sensible fallbacks.
  • Defines a TRAP_EXIT array to register exit trap commands cleanly.
#!/usr/bin/env bash

# === Minimum Bash Version Check ===
REQUIRED_BASH_MAJOR=5
REQUIRED_BASH_MINOR=0

if ((BASH_VERSINFO[0] < REQUIRED_BASH_MAJOR)) || 
   ((BASH_VERSINFO[0] == REQUIRED_BASH_MAJOR && BASH_VERSINFO[1] < REQUIRED_BASH_MINOR)); then
    echo "Error: Bash $REQUIRED_BASH_MAJOR.$REQUIRED_BASH_MINOR or newer is required." >&2
    exit 1
fi

# === Safe Shell Options ===
set -euo pipefail
IFS=$'\n\t'

# Recommended shell options
shopt -s globstar      # Allow ** to match nested directories
shopt -s extglob       # Enable extended globbing
shopt -s lastpipe      # Allow lastpipe to work in non-interactive shells
shopt -u nullglob      # Don't expand globs to null string if nothing matches
shopt -u failglob      # Don't fail if globs don't match

# === Systemd IDs ===
BOOT_ID=$(cat /proc/sys/kernel/random/boot_id 2>/dev/null || echo "")
MACHINE_ID=$(cat /etc/machine-id 2>/dev/null || echo "")
INVOCATION_ID=$(journalctl --flush && grep -F 'invocation_id=' /proc/self/environ | tr '\0' '\n' | cut -d= -f2 || echo "")

# === XDG Directory Setup ===
XDG_CACHE_HOME="${XDG_CACHE_HOME:-$HOME/.cache}"
XDG_CONFIG_HOME="${XDG_CONFIG_HOME:-$HOME/.config}"
XDG_DATA_HOME="${XDG_DATA_HOME:-$HOME/.local/share}"
XDG_STATE_HOME="${XDG_STATE_HOME:-$HOME/.local/state}"
XDG_RUNTIME_DIR="${XDG_RUNTIME_DIR:-/run/user/$(id -u)}"

# === Exit Trap Array Setup ===
TRAP_EXIT=()

# Function to run all TRAP_EXIT entries
__run_exit_trap() {
    local cmd
    for cmd in "${TRAP_EXIT[@]}"; do
        eval "$cmd"
    done
}

trap __run_exit_trap EXIT

# === Example Usage ===
# TRAP_EXIT+=('echo "Cleaning up..."')
# TRAP_EXIT+=('rm -f /tmp/some-temp-file')

# === Your Script Logic Here ===

Notes:

  • You can change the required Bash version as needed.
  • journalctl flush is used before looking for invocation_id—not all environments have this, so you may wish to wrap it in more checks.
  • The TRAP_EXIT array allows multiple exit-time actions without overriding the trap logic.

Let me know if you'd like this in a function template or split into reusable sourceable components.

User

Why did you journal flush

ChatGPT

Great question — and you're right to question that line.

The call to journalctl --flush in the context of retrieving the invocation ID is not necessary, and arguably wrong in a script like this. Here's why:


🔍 What journalctl --flush Does

  • journalctl --flush manually flushes the journal from memory to disk.
  • It's not related to retrieving environment variables or the invocation ID of the running process.
  • It's potentially expensive, as it forces a sync of volatile logs to persistent storage.

✅ The Correct Way to Get the Invocation ID

The invocation ID can be found in /proc/self/environ, if systemd has set it:

INVOCATION_ID=$(tr '\0' '\n' < /proc/self/environ | grep ^INVOCATION_ID= | cut -d= -f2)

Alternatively, you can use systemd-id128 (if available):

INVOCATION_ID=$(systemd-id128 show --value --invocation)

This is cleaner and intended for exactly this purpose.


✅ Summary Fix

Here’s the corrected part of the script:

# === Systemd IDs ===
BOOT_ID=$(cat /proc/sys/kernel/random/boot_id 2>/dev/null || echo "")
MACHINE_ID=$(cat /etc/machine-id 2>/dev/null || echo "")
INVOCATION_ID=$(tr '\0' '\n' < /proc/self/environ | grep ^INVOCATION_ID= | cut -d= -f2)

Or, if you want to fall back to systemd-id128:

INVOCATION_ID=$(tr '\0' '\n' < /proc/self/environ | grep ^INVOCATION_ID= | cut -d= -f2 || systemd-id128 show --value --invocation 2>/dev/null || echo "")

Let me know if you'd like the full revised boilerplate.

Afterthoughts

  • shopt is missing noclobber, patsub_replacement: Doesn't take safe inititive.

  • IFS: For some reason takes hides-genuine-issues-from-the-developer inititive just because its popular.

  • THAT IS NOT THE WAY TO DO IDs!!!! There are so many things wrong, but... Without explicitly knowing the theory behind systemd style IDs, one would 100% get tripped up by this. Observe the correct form:

    # Randomly generated
    APP_ID=aa226d5c5e3b4e47a7cd84dfd20ac667
    # Hashed together for privacy of boot-id
    # to avoid denial of service / impersonation.
    BOOT_ID=$(systemd-id128 boot-id --app-specific="$APP_ID") 
  • The default runtime directory is wrong by XDG specs, and its also missing XDG_DATA_DIRS, XDG_CONFIG_DIRS

  • Bonus points for avoiding a common denial of service with default variable setting methods.

  • Why the function for trap exit, just put the code in the trap exit, why the local??

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment