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_COMMANDvariable - 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:
- Places module files inside it:
hhgttg.sh(main logic)hhgttg.config.sh(settings/configurations)bash-preexec.sh(dependency)
- Appends an activation block to
~/.bashrconce, with markers. - 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:
bash-preexec.shloads first- Your module loads second
- Shell remains interactive-only
Correct loading sequence is essential because:
bash-preexecdefines 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:
preexecprecmd- âon command startâ
- âon command endâ
- âon prompt drawâ
Bash only has these primitives:
PROMPT_COMMANDtrap DEBUGPS0(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.