đŸȘ hhgttg: A Friendly Bash Module for Smarter Shells

Discover how the hhgttg Bash module enhances your terminal with smart preexec and precmd hooks. Learn how it works, how bash-preexec powers it, and why this tiny module brings big, user-friendly upgrades to your interactive shell—without the panic.

đŸȘ hhgttg: A Friendly Bash Module for Smarter Shells
Photo by Markus Spiske / Unsplash

1. Introduction: Don’t Panic, It’s Just Your Shell Becoming Smarter

Every command-line user eventually reaches the same point: you want your shell to do more.

Not in a bloated “install a spaceship” way, but in a clean, subtle, and “would you like a towel with that?” kind of way.

This is where hhgttg comes in — a small, friendly Bash module designed to hook into your shell’s lifecycle, respond to events, track what’s going on, and generally behave like a polite assistant who whispers useful things when needed and stays out of the way when not.

So in this article, we will look only at the module and its internal mechanics. Just the module itself and how Bash makes all of this possible.

2. What Is the hhgttg Module?

The module is a plug-in style extension for Bash that:

  • Loads automatically in interactive shells
  • Hooks into the shell lifecycle (before commands run, before the prompt prints)
  • Enables custom callbacks
  • Allows you to build features like timers, logging, command tracking, status indicators, CLI enhancements, etc.
  • Acts as a reusable, portable utility module

In short, hhgttg gives you an easy way to run code at exactly the right times in the shell’s workflow.

Bash normally does not offer pre-exec or pre-prompt hooks. It has no native “execute this before a command runs” feature.

That’s where bash-preexec.sh enters the story.

3. The Secret Ingredient: bash-preexec.sh

Before we dive into your module, we need to understand its most important dependency.

3.1 What does bash-preexec do?

bash-preexec is a standalone 600-ish line script that simulates two missing Bash features:

  • preexec() – runs immediately before a command executes
  • precmd() – runs immediately before the prompt is displayed

These hooks exist in zsh by default, but not in Bash.

So bash-preexec uses clever Bash tricks to emulate them:

  • it instruments the PROMPT_COMMAND variable
  • it intercepts debug traps (trap DEBUG)
  • it wraps command evaluation
  • it ensures handlers run only for real commands (not shell internals)

The end result is:

You can write functions that run at very specific, predictable events in the shell lifecycle.

This is foundational for hhgttg.

4. How the Installer Works (High-Level)

Before loading the module, you need the module installed properly.
Theinstall.sh script handles this job safely, idempotently, and portably.

What the installer does:

  1. Places module files inside it:
    • hhgttg.sh (main logic)
    • hhgttg.config.sh (settings/configurations)
    • bash-preexec.sh (dependency)
  2. Appends an activation block to ~/.bashrc once, with markers.
  3. Ensures the shell loads:
    • bash-preexec
    • the module
      only in interactive sessions ([[ $- == *i* ]]).

Creates a clean module directory
Default:

~/.local/shell.d/hhgttg

This guarantees:

  • no re-appending
  • no pollution of non-interactive shells (CI, scripts, cron, etc.)
  • no broken PROMPT_COMMAND
  • no accidental overrides

5. How hhgttg Integrates With bash-preexec

Now we get to the real engine room.

The module attaches custom functions to the two hooks that bash-preexec provides.

5.1 preexec() – before a command runs

hhgttg can:

  • log the command
  • record its start time
  • track command history
  • set internal state
  • modify behavior dynamically

Example internal flow:

User presses ENTER →
  bash-preexec intercepts →
    preexec() runs →
      hhgttg_preexec() runs →
        hhgttg collects info, timestamp, state →
          command runs

This hook is incredibly useful for:

  • timers
  • statistics
  • debugging
  • real-time tracking

5.2 precmd() – before the prompt appears

This hook fires after the command finishes, but before the shell draws the next prompt.

hhgttg can use this moment to:

  • calculate elapsed time
  • display indicators
  • update status
  • clean up internal state
  • attach information to the prompt
  • write logs
  • communicate results

The lifecycle:

command finishes →
  bash-preexec intercepts →
    precmd() fires →
      hhgttg_precmd() runs →
        custom prompt data is updated →
          PS1 draws the prompt

Now, let’s walk through the module’s internal design.

6. Understanding the Module’s Files

The module consists of three main files:

hhgttg.sh
hhgttg.config.sh
bash-preexec.sh

6.1 hhgttg.config.sh

This file typically contains:

  • user-configurable settings
  • toggles for features
  • default parameters
  • optional environment variables

This makes the module flexible without touching the core logic.

Examples:

HHGTTG_ENABLE_TIMERS=1
HHGTTG_LOGFILE="$HOME/.local/share/hhgttg/log.txt"

(Exact content depends on your implementation.)

6.2 hhgttg.sh – the main logic

This is the heart of the module.

It typically:

  • defines preexec/precmd handlers
  • loads configuration
  • sets up storage variables
  • attaches your functions to bash-preexec’s arrays
  • provides helper utilities
  • offers API-like functions (so users can extend it)

Depending on your features, it may include:

  • timers
  • command tracking
  • logging
  • “last command status” indicators
  • prompt enhancements
  • notifications
  • debug info

Example pattern (simplified):

hhgttg_preexec() {
    HHGTTG_LAST_COMMAND="$1"
    HHGTTG_START_TIME=$(date +%s%3N)
}

hhgttg_precmd() {
    local exit_code="$?"
    local end=$(date +%s%3N)
    local duration=$((end - HHGTTG_START_TIME))

    # do things like logging, updating the prompt, etc.
}

6.3 Loading order (critical!)

The .bashrc block ensures:

  1. bash-preexec.sh loads first
  2. Your module loads second
  3. Shell remains interactive-only

Correct loading sequence is essential because:

  • bash-preexec defines the hook arrays
  • your module registers functions into those arrays
  • if reversed, nothing works

Your installer guarantees the correct order.

7. Life Inside the Shell: The hhgttg Runtime Flow

Let’s follow a typical interactive shell session:

Step 1: User opens a shell

Your .bashrc block loads:

bash-preexec.sh
hhgttg.sh

Hook arrays are initialized.

Step 2: User types a command

For example:

ls -l /tmp

Step 3: preexec() fires

Just before Bash executes ls, hhgttg:

  • captures the command
  • records start time
  • stores context

Step 4: command runs

The command executes normally.

Step 5: precmd() fires

After the command completes, Bash is about to draw a prompt.

hhgttg:

  • computes elapsed time
  • checks exit status
  • logs or displays info
  • updates state variables
  • updates prompt decorations

Step 6: prompt appears

Everything resets and waits for the next command.

This cycle repeats forever.

8. Why Use bash-preexec Instead of Your Own Hooks?

Because Bash has no:

  • preexec
  • precmd
  • “on command start”
  • “on command end”
  • “on prompt draw”

Bash only has these primitives:

  • PROMPT_COMMAND
  • trap DEBUG
  • PS0 (rarely used)
  • command history expansion funkiness

bash-preexec combines these in a carefully engineered way:

  • wraps PROMPT_COMMAND without overwriting it
  • respects existing debug traps
  • avoids recursion
  • filters out non-command events
  • provides predictable timing
  • supports multiple handlers via arrays

If you tried to replicate it manually, you'd quickly end up writing exactly what bash-preexec already does — and you'd still get edge cases wrong.

So relying on bash-preexec is the right choice.

9. Benefits of the hhgttg Architecture

✔ Modular

The entire system lives in its own directory and has zero side effects outside of the shell session.

✔ Extendable

You can add more features simply by attaching new handlers.

✔ Safe

Interactive-only loading means:

  • scripts
  • cron jobs
  • CI pipelines
  • systemd units


are completely unaffected.

✔ Debuggable

State is isolated and easy to inspect.

✔ Portable

Works in:

  • standard Bash
  • container environments
  • minimal shells
  • remote sessions
  • local terminals

✔ Feature-ready

You can build:

  • timers
  • prompts
  • auditing
  • analytics
  • logging
  • command previews
  • command status decorations
  • notifications
  • personal productivity tools

All from the same foundation.

10. Installation: How the Installer Keeps Everything Clean

You already saw the installer code, so here’s the conceptual flow:

10.1 Ensures module directory exists

Creates:

~/.local/shell.d/hhgttg/

10.2 Installs or downloads files

Prefer local files → fallback to GitHub raw URLs → handle curl/wget availability.

10.3 Ensures correct permissions

Readable, non-executable.

10.4 Injects .bashrc block once

With idempotency markers:

# hhgttg: start
...
# hhgttg: end

10.5 Loads only for interactive shells

So nothing breaks in automation.

11. How Users Enable or Disable the Module

Because the installation modifies .bashrc, users have easy control:

  • to disable: comment out the block
  • to re-enable: uncomment
  • to uninstall: remove the block + delete directory
  • to reinstall: run installer again (idempotent)

This simplicity makes it approachable even for non-expert users.

12. Conclusion: A Small Module With Galactic Potential

hhgttg may be compact, but it sits on top of one of Bash’s most powerful extensions: the ability to hook into shell lifecycle events.

With bash-preexec providing the missing pieces and your module providing the logic, the result is a clean, reliable, extensible framework for enhancing the user experience in the terminal.

It doesn’t replace your shell — it simply gives it a towel, a pocket guide, and a better set of instructions before it panics.

And now that the internals are covered, the next step is showing the world the project and preparing it for real distribution.

But that, as they say, is a story for the next post.

Read next

Ready-to-Use Starship Prompt Templates (Part 2)

Ready to upgrade your terminal? After publishing my deep-dive guide on customizing prompts with Starship, I spent a few days experimenting with modules, colors, and layouts. In this post, I’m sharing several polished, ready-to-use Starship prompt templates you can drop into your setup immediately.