Frobulator

SHELL PARSER AND SCRIPTING FUNCTION LIBRARY

Frobulator is a custom shell parser and scripting function library, easy to use and understand, that streamlines your shell scripting projects.

Source one file and get colorized line markers, prompts, progress feedback, checkpoints, and file, network and package helpers for apt and dnf.

FROBULATE ALL THE THINGS!

Version // 2026-10-23-1100

OVERVIEW // A FUNCTION LIBRARY FOR SHELL SCRIPTS

Frobulator is a shell parser and scripting function library. Source it at the top of a Bash script and every message, prompt, download, package install and checkpoint gets the same clean, colorized, 80 column output - all while making redundant and complex code bits a thing of the past.

Frobulator marker lines in a terminal

Scripts only

The current set of assertions upon which Frobulator is built restricts its functionality to scripts exclusively, at least for the time being.

INSTALL // ONE FILE IN ~/.LOCAL/BIN

Frobulator is a single Bash file. Run it to install it to ${HOME}/.local/bin, then source it from your scripts - or let the bootstrap header do both.

Install

curl -s -L get.frbltr.app > frobulator && bash frobulator

Running the script directly installs it - no arguments default to --install. Sourcing is unaffected: the options only apply when the file is executed.

./frobulator              # install (default)
./frobulator --install    # install
./frobulator --uninstall  # uninstall - scripts using frobulator fetch it again on their next run
./frobulator --help       # usage

Source

. "${HOME}"/.local/bin/frobulator

Version

The release is identified by the literal update="YYYY-MM-DD-HHMM" line at the top of the file. The bootstrap reads it as text (grep '^update=') and downloads a new copy when the online value sorts after the installed one - bump it, with a four digit time, for every release, including same day releases. version is derived from it.

View the library source ›

BOOTSTRAP // THE STANDARD SCRIPT HEADER

Start every script with this header. It fetches the latest Frobulator with curl or wget, sources it once, escalates when needed and prints the script banner. The sections below it are the standard layout of a Frobulator script.

  1. Arguments. self_arguments keeps the original arguments for frobulator.escalate - spaces, quotes and empty arguments intact.
  2. Sudo aware. Run as root through sudo, the header keeps using the invoking user's copy in their home directory.
  3. Newer only. The download from get.frbltr.app replaces the local copy only when its update= line sorts after it.
  4. Loaded once. frobulator_loaded skips the download and sourcing when Frobulator is already loaded.
  5. Requirements. The list array feeds frobulator.require, which installs whatever command or package is missing.
#!/bin/bash

# dependencies ─────────────────────────────────────────────────────────────────

script="$(basename -- "${BASH_SOURCE[0]}")"

# script arguments for escalation

self_arguments=("${@}")

if [ "${frobulator_loaded}" != "true" ]
then
	echo
	echo "[  >  ] Checking..."
	echo

	# root through sudo keeps using the user's frobulator

	if [ -n "${SUDO_USER}" ]
	then
		USER="${SUDO_USER}"
		HOME="$(getent passwd "${SUDO_USER}" | cut -d ':' -f 6)"
	fi

	frobulator="${HOME}/.local/bin/frobulator"
	frobulator_download="$(mktemp)"

	mkdir -p "${HOME}/.local/bin"

	# latest release - curl or wget - local copy replaced only by a newer one

	curl -s -L -o "${frobulator_download}" get.frbltr.app 2> /dev/null ||
	wget -q -O "${frobulator_download}" get.frbltr.app 2> /dev/null

	version_online="$(grep -m 1 '^update=' "${frobulator_download}")"
	version_local="$(grep -m 1 '^update=' "${frobulator}" 2> /dev/null)"

	if [[ "${version_online}" > "${version_local}" ]]
	then
		mv -f "${frobulator_download}" "${frobulator}"

		chmod +x "${frobulator}"
	fi

	rm -f "${frobulator_download}"

	if [ ! -f "${frobulator}" ]
	then
		echo "[  !  ] Unable to fetch frobulator ── [ get.frbltr.app ]"
		echo

		exit 1
	fi

	source "${frobulator}"
fi

# superuser ────────────────────────────────────────────────────────────────────

frobulator.escalate

# script ───────────────────────────────────────────────────────────────────────

frobulator.script

# version ──────────────────────────────────────────────────────────────────────

version="YYYY-MM-DD"

# usage ────────────────────────────────────────────────────────────────────────

# variables ────────────────────────────────────────────────────────────────────

# defaults ─────────────────────────────────────────────────────────────────────

# functions ────────────────────────────────────────────────────────────────────

# update ───────────────────────────────────────────────────────────────────────

# frobulator.update

# frobulator.upgrade

# requirements ─────────────────────────────────────────────────────────────────

list=(

)

frobulator.require ${list[@]}

list=()

# configuration ────────────────────────────────────────────────────────────────

Get the bootstrap header ›

MARKERS // 23 LINE TYPES

Every marker prints a message, an optional detail and an optional fill character on one line as wide as the terminal, followed by a blank line from frobulator.brk - scripts do not add echo after messages. Prompts stay on the same line for the answer.

MarkerCommandPurposeColor
[ i ]frobulator.nilempty marker linewhite
[ i ]frobulator.infinformation lineblue
[ ! ]frobulator.wrnwarning lineorange
[ # ]frobulator.msgmessage linenavy
[ + ]frobulator.addadd/create linelime
[ - ]frobulator.remremove/delete linecrimson
[ ● ]frobulator.retretain/keep lineaqua
[ ○ ]frobulator.relrelease lineteal
[ ▷ ]frobulator.fwdforward/progress linesilver
[ ◁ ]frobulator.revreverse/back linesilver
[ □ ]frobulator.stpstop linegrey
[ ▽ ]frobulator.dwldownload linemagenta
[ △ ]frobulator.uplupload linepurple
[ □ ]frobulator.lnklink lineyellow
[ ✓ ]frobulator.scssuccess linegreen
[ 𐄂 ]frobulator.errerror linered
[ > ]frobulator.insinsert/input lineteal
[ ¤ ]frobulator.cptcomplete linemagenta
[ / ]frobulator.urlurl linesilver
[ ? ]frobulator.qstquestion prompt (no newline)fuschia
[ + ]frobulator.iptinput prompt (no newline)pink
[ ~ ]frobulator.usruser prompt (no newline)grey
[ ↵ ]frobulator.rtnreturn prompt (no newline)aqua

Arguments

Markers accept direct strings, variables, arrays, command substitutions or values generated elsewhere in the script. All of them are normalized through frobulator.pmt, so alignment, wrapping and span generation stay consistent.

frobulator.inf "Checking dependencies" "[ curl ]"

prompt_arguments=(
	"Checking dependencies"
	"[ curl ]"
)

frobulator.inf "${prompt_arguments[@]}"
[ i ] Checking dependencies ───────────────────────────────────────── [ curl ]

COLORS // 18 NAMED 256 COLOR CODES

Every palette color has its own line command, and its name is accepted wherever a color is: numbered steps, separators and the critter. Colors are set only when output is a terminal - through tput, or ANSI sequences without it - and stay empty otherwise.

white255 // #eeeeee
silver248 // #a8a8a8
grey243 // #767676
black238 // #6b7080
red1 // #cc575d
crimson88 // #c0393f
green2 // #5ac25a
lime28 // #008700
yellow11 // #f6d32d
orange202 // #ff5f00
blue4 // #5294e2
navy26 // #005fd7
magenta5 // #c061cb
purple53 // #9a5fb5
fuschia126 // #d33fa8
pink200 // #ff00d7
aqua6 // #00aade
teal30 // #008787

Add your own

frobulator.palette adds a named color at run time from a 256 color code. Adding an existing name updates its color. The colors shown here follow a dark terminal theme, with the darkest codes lifted for reading: your terminal decides how they look.

frobulator.palette cornflower 68
frobulator.num cornflower 1 "Soft blue line" "[ value ]"

CHECKPOINTS // STATUS, LOGS AND RESUMES

Frobulator replaces the if [ "${?}" -ne 0 ] blocks of a script with one line checks. Failures are recorded, logged to ~/.local/var/log/<script>-<stamp>.log and summed up at the end.

CommandOn failure
frobulator.completeReports Incomplete - <label>, logs the failure and carries on
frobulator.complete haltLogs, prints the summary and exits the script - also inside a function
frobulator.haltPrints the message and exits with the failed command's own status
frobulator.failReturns the failed status inside a function - follow it with || return
frobulator.trackRecords the failure in the calling function's status
frobulator.resultReports overall success or the failure count, then clears the records
zipalign -p -f 4 "${file_unsigned}" "${file_aligned}"
frobulator.complete halt "Align failed" "[ ${file_aligned} ]"

rm -f "${cache}"
frobulator.complete

frobulator.complete "${checkpoint_directory}" "make-all" make all

frobulator.result "${script}"

The checkpoint form runs a command once: it is skipped while "${path}/${checkpoint}" exists, and the checkpoint file is only created on success. Run the script again after a failure and it resumes where it stopped.

PACKAGES // APT AND DNF

The package manager is picked from the distribution family in /etc/os-release: debian and its derivatives use apt, fedora and its derivatives use dnf, or yum when dnf is missing. The family decides, not the commands on the PATH, so a Fedora container under Termux uses dnf even with Termux's apt in reach.

Commandaptdnf
frobulator.updateapt-get updatednf makecache
frobulator.upgradeapt-get dist-upgradednf upgrade
frobulator.installapt search, then installdnf install
frobulator.requireapt-file searchdnf repoquery --whatprovides
frobulator.reinstallapt-get install --reinstalldnf reinstall
frobulator.purgeapt-get purge --autoremovednf remove
frobulator.holdapt-mark holddnf versionlock add
frobulator.releaseapt-mark unholddnf versionlock delete
frobulator.cleanautoremove, autoclean, cleandnf autoremove, dnf clean all

frobulator.require checks commands and installed packages first and only touches the package manager when something is missing, so requirements that are already met need no root access. Package name globs such as "libssl*" install every match.

PERMISSIONS // SCOPES INSTEAD OF MODES

frobulator.permissions sets the exact mode from a scope and optional modifiers, in place of raw chmod numbers. Directories always keep execute for whoever can read them.

callfiledirectorytypical use
public644 rw-r--r--755 rwxr-xr-xregular files, configs, shared data
public execute755 rwxr-xr-x755 rwxr-xr-xscripts, binaries, installed tools
public readonly444 r--r--r--555 r-xr-xr-xreference files that should not change
public readonly execute555 r-xr-xr-x555 r-xr-xr-xlocked-down tools
group640 rw-r-----750 rwxr-x---files for your group only
group execute750 rwxr-x---750 rwxr-x---group-only scripts
group readonly440 r--r-----550 r-xr-x---group reference files
group readonly execute550 r-xr-x---550 r-xr-x---locked group tools
private600 rw-------700 rwx------credentials, keys, tokens
private execute700 rwx------700 rwx------personal scripts
private readonly400 r--------500 r-x------ssh keys, certificates
private readonly execute500 r-x------500 r-x------locked personal tools
frobulator.permissions public execute "${path}/${file}"
frobulator.permissions private readonly "${HOME}/.ssh/id_ed25519"

FUNCTIONS // EVERY COMMAND AND EXAMPLE

All 110 Frobulator functions with their arguments and examples. Search them, or pick a group.

frobulator.plo

Markers

[ p ] rompt [ l ] ine [ o ] verwrite:

Clears a defined number of lines above the current cursor position, wiping from that point down to the end of the screen — built on terminal_cursor_up and terminal_clear_down.

frobulator.plo 1

frobulator.pmt

Markers

[ p ] rompt [ m ] anagement [ t ] ool:

The shared prompt-formatting engine behind every color and marker command. Normalizes 1–3 arguments (begin, end, span character) into a line as wide as the terminal (columns_terminal, never below 80): pads/fills the middle span, folds begin with word-detection when it would overflow the line, and truncates an overlong end with an ellipsis while preserving its surrounding bracket style - a [ 'name' // value ] bracket keeps its whole // value ] tail (and the closing quote), so only the name is shortened. Populates the prompt_string array consumed by every wrapper below rather than printing directly.

frobulator.pmt "Downloading" "[ package.tar.gz ]"

frobulator.brk

Markers

Line break: prints the blank line that follows every marker line and notice block, and flags it so the carry-over markers (frobulator.nul, frobulator.ind) can rejoin the line above instead. Called by the markers themselves - use it directly only after your own echo output. It also adds the marker line and its blank line to lines_printed_terminal (wrapped rows counted at the real terminal width), which frobulator.escalate uses to clear its output.

frobulator.brk

frobulator.ltr

Markers

Letter.

Prints a lettered step marker — first argument is the letter, remaining arguments are forwarded to frobulator.pmt.

frobulator.ltr a "Select source directory"

frobulator.num

Markers

Number.

Prints a numbered step marker — first argument is the number, remaining arguments are forwarded to frobulator.pmt. An optional palette color before the number prints the number and the line in that color - e.g. green for files to add and red for files to remove, matching their frobulator.add / frobulator.rem headers.

frobulator.num 1 "Install dependencies"
frobulator.num lime 1 "new-track.mp3"
frobulator.num crimson 2 "old-track.mp3"

frobulator.sep

Markers

Separator.

Prints a full-width separator line built from frobulator.pmt. The line is uncolored unless an optional palette color is given first, which prints the line and its text in that color.

frobulator.sep
frobulator.sep lime "Section"

frobulator.ntf

Markers

Notify.

Prints a framed notice block. Optional leading arguments select a frame style (square [default], round, heavy, double, dots, matrix, tech, skel, ascii), the split keyword (renders title and message as two separate frames instead of one divided frame), and a marker-type keyword (inf, wrn, scs, err, etc.) to color the title using that marker's color. Remaining arguments are title then message; a second marker-type keyword placed between them colors the message. Like the line markers, the block is followed by a blank line.

frobulator.ntf round inf "Notice" "The setup process is ready."
frobulator.ntf heavy split err "Failure" "Could not reach the update server."
frobulator.ntf wrn "Disclaimer" inf "This action is permanent."

frobulator.nil

Markers

Empty (nil) line. Prints a empty marker line with the [ i ] marker in white.

frobulator.nil "Message" "[ detail ]"

frobulator.inf

Markers

Information. Prints a information line with the [ i ] marker in blue.

frobulator.inf "Message" "[ detail ]"

frobulator.wrn

Markers

Warning. Prints a warning line with the [ ! ] marker in orange.

frobulator.wrn "Message" "[ detail ]"

frobulator.msg

Markers

Message. Prints a message line with the [ # ] marker in navy.

frobulator.msg "Message" "[ detail ]"

frobulator.add

Markers

Add. Prints a add/create line with the [ + ] marker in lime.

frobulator.add "Message" "[ detail ]"

frobulator.rem

Markers

Remove. Prints a remove/delete line with the [ - ] marker in crimson.

frobulator.rem "Message" "[ detail ]"

frobulator.ret

Markers

Retain. Prints a retain/keep line with the [ ● ] marker in aqua.

frobulator.ret "Message" "[ detail ]"

frobulator.rel

Markers

Release. Prints a release line with the [ ○ ] marker in teal.

frobulator.rel "Message" "[ detail ]"

frobulator.fwd

Markers

Forward. Prints a forward/progress line with the [ ▷ ] marker in silver.

frobulator.fwd "Message" "[ detail ]"

frobulator.rev

Markers

Reverse. Prints a reverse/back line with the [ ◁ ] marker in silver.

frobulator.rev "Message" "[ detail ]"

frobulator.stp

Markers

Stop. Prints a stop line with the [ □ ] marker in grey.

frobulator.stp "Message" "[ detail ]"

frobulator.dwl

Markers

Download. Prints a download line with the [ ▽ ] marker in magenta.

frobulator.dwl "Message" "[ detail ]"

frobulator.upl

Markers

Upload. Prints a upload line with the [ △ ] marker in purple.

frobulator.upl "Message" "[ detail ]"

frobulator.lnk

Markers

Link. Prints a link line with the [ □ ] marker in yellow.

frobulator.lnk "Message" "[ detail ]"

frobulator.scs

Markers

Success. Prints a success line with the [ ✓ ] marker in green.

frobulator.scs "Message" "[ detail ]"

frobulator.err

Markers

Error. Prints a error line with the [ 𐄂 ] marker in red.

frobulator.err "Message" "[ detail ]"

frobulator.ins

Markers

Insert. Prints a insert/input line with the [ > ] marker in teal.

frobulator.ins "Message" "[ detail ]"

frobulator.cpt

Markers

Complete. Prints a complete line with the [ ¤ ] marker in magenta.

frobulator.cpt "Message" "[ detail ]"

frobulator.url

Markers

Url. Prints a url line with the [ / ] marker in silver.

frobulator.url "https://example.com"

frobulator.qst

Markers

Question prompt. Prints a question prompt with the [ ? ] marker in fuschia, and stays on the same line for the answer.

frobulator.qst "Enter value"

frobulator.ipt

Markers

Input prompt. Prints a input prompt with the [ + ] marker in pink, and stays on the same line for the answer.

frobulator.ipt "Enter value"

frobulator.usr

Markers

User prompt. Prints a user prompt with the [ ~ ] marker in grey, and stays on the same line for the answer.

frobulator.usr "Enter value"

frobulator.rtn

Markers

Return prompt. Prints a return prompt with the [ ↵ ] marker in aqua, and stays on the same line for the answer.

frobulator.rtn "Press to close" "[ konsole // 'weather' ]"

frobulator.nul

Markers

Null line - carry-over: keep color of the line above. Carry-over line: continue line, retain color. Rejoins the line above on screen, so a message and its details stay together.

frobulator.nul "continued output"

frobulator.ind

Markers

Index line - carry-over: without color. Carry-over line: continue line, clear color. Rejoins the line above on screen, so a message and its details stay together.

frobulator.ind "continued output"

frobulator.erase

Formatting

Thin wrapper around frobulator.plo — erases a defined number of lines above the current cursor position the same way.

frobulator.erase 3

frobulator.columns

Formatting

Sets columns_terminal - the width used by prompts, notifications, bars and images - from the terminal width (tput cols), never below columns_terminal_minimum (default 80), which is also used when output is not a terminal. Runs on load and again on every window resize (WINCH trap), so new output follows the window width.

columns_terminal_minimum=100
frobulator.columns

frobulator.[color]

Colors

Each named color wrapper calls frobulator.pmt, stores its color into prompt_string and echoes the result, so every color line follows the same formatting rules as the markers.

frobulator.[color] "[string]" "[string]" "[span character]"
frobulator.lime "Highlighted" "[ value ]"
frobulator.whitefrobulator.silverfrobulator.greyfrobulator.blackfrobulator.redfrobulator.crimsonfrobulator.greenfrobulator.limefrobulator.yellowfrobulator.orangefrobulator.bluefrobulator.navyfrobulator.magentafrobulator.purplefrobulator.fuschiafrobulator.pinkfrobulator.aquafrobulator.teal

frobulator.palette

Colors

Adds a named color to the palette at run time from a 256-color code (0 to 255). The name is then accepted wherever a palette color name is - the optional color of frobulator.num and frobulator.sep, and the critter. Like the built-in colors, the sequence is set only when output is a terminal (via tput, or ANSI without it) and is empty otherwise. Adding an existing name updates its color.

frobulator.palette cornflower 68
frobulator.num cornflower 1 "Soft blue line" "[ value ]"

frobulator.separate

Prompts

Prints a predefined frobulator.sep line (which prints its own blank line after it) — use to separate instructions or warnings from prompts.

frobulator.separate

frobulator.read

Prompts

Wraps the read builtin (forwarding all arguments to it) and prints a trailing blank line, so prompts stay evenly spaced after user input.

frobulator.qst "Continue?" "[ y/n ]"
frobulator.read reply

frobulator.script

Prompts

Prints a script startup banner: a msg-colored notice block titled Script showing Executing: '${script}' and Version:. An optional description is shown above them.

frobulator.script
frobulator.script "Welcome to the Gutendex terminal library!"

frobulator.type

Prompts

Prints a string one character at a time with a randomized delay between each, to emulate human typing. The first argument is the speed, a single digit from 0 to 9 (optional, defaults to 9). Each step adds 0.02 s to the maximum delay per character: 0 types at a steady 0.02 s, about one screen refresh, and 9 at up to 0.2 s. Every argument after it is printed, joined with spaces, so the string needs no quotes. When the first argument is not a single digit, it is part of the string.

frobulator.type 3 "Preparing environment..."

frobulator.timeout

Prompts

Sleeps for a number of seconds (defaults to 1 when omitted) — a simple pause between commands.

frobulator.timeout 5

frobulator.clear

Prompts

Pauses for frobulator.timeout's default interval, then clears the terminal — used for script "paging" once a step's checkpoints are met.

frobulator.clear

frobulator.countdown

Prompts

Shows a live countdown before continuing, validating that the first argument is a non-negative integer. Remaining arguments are forwarded to frobulator.pmt as the message shown beside the counter.

frobulator.countdown 10 "Starting install" "[ press ctrl+c to cancel ]"

frobulator.action

Progress

Generates a randomly colored, grammatically conjugated action prompt from a verb (e.g. download → Downloading..., panic → Panicking...), falling back to frobulate when no verb is given. Handles common English suffix rules (-ie → -y, silent -e drop, -ic → -ick, and final consonant doubling for one-syllable verbs ending consonant-vowel-consonant plus a short list of verbs stressed on the last syllable, e.g. strip → Stripping, submit → Submitting, but open → Opening) before appending -ing. Sets the prompt_progress/color_progress globals consumed by frobulator.progress. Callable directly, but normally invoked internally.

frobulator.action "download" "[ ${file} ]"

frobulator.process

Progress

Waits on the most recently backgrounded process (${!}), animating a simple /\ ticker beside a message until it exits, then returns that process's exit status.

frobulator.background process "Updating package index" -- apt-get update

Called directly, it follows the job started just before it: apt-get update & then frobulator.process "Updating package index" — frobulator.background does both without the shell's job notices.

frobulator.progress

Progress

Same as frobulator.process, but generates its message via frobulator.action (so the first argument is a bare verb, not a pre-built message) and animates a smoother ⎺⎻⎼⎽⎼⎻ ticker.

frobulator.background download "[ ${file} ]" -- curl -L "${url}" -o "${file}"

Called directly, it follows the job started just before it: curl … & then frobulator.progress "download" "[ ${file} ]" — frobulator.background does both without the shell's job notices.

frobulator.bar

Progress

Runs a command (via "${SHELL}" -c) with a simulated activity bar — progress accelerates early, slows near 90%, and only reaches 100% once the process actually exits. Not a true byte-accurate progress meter.

frobulator.bar sleep 5
frobulator.bar apt-get update
frobulator.bar rsync -av source/ destination/

frobulator.temporary

Progress

Creates one or more temporary directories (template frobulator.temporary.XXXXXX, via mktemp -d) and, for each, eval-assigns the resulting path back into the named variable you pass in — so the argument is a variable name, not a path. Several variable names can be passed; the caller's own variables are left untouched.

frobulator.temporary directory_temporary
# ${directory_temporary} now holds the generated path

frobulator.trap

Progress

Registers EXIT/HUP/INT/PIPE/QUIT/TERM traps that recursively delete the given directories on interruption or normal exit. Paths are fixed when the trap is set, and directories from every call are kept - so several temporary directories can be cleaned up. Use immediately after frobulator.temporary.

frobulator.temporary directory_temporary
frobulator.trap "${directory_temporary}"

frobulator.complete

Progress

One-line status check after a command, with frobulator.result once at the end of the script. The first argument picks what a failure means:

  • continue (default, also when omitted) - records the status, reports Complete - <label> or Incomplete - <label>, logs failures, and carries on.
  • halt - same on success; on failure prints the message (or Incomplete - <label>), logs it, prints the frobulator.result summary and exits the script - also when called inside a function.

An optional message and detail follow the keyword; the message replaces the label on failure. Without a message the label is the calling function's name (image_download → image download). At script level it is the previous command in the script (frobulator.write → write, cd → cd), or the action of frobulator.progress or frobulator.background (clone); assignments and lines it cannot read fall back to Operation N. Failures are written to the script's log (~/.local/var/log/<script>-<stamp>.log) through frobulator.silence log.

apk_tool_update
frobulator.complete halt

zipalign -p -f 4 "${file_unsigned}" "${file_aligned}"
frobulator.complete halt "Align failed" "[ ${file_aligned} ]"

rm -f "${cache}"
frobulator.complete

frobulator.result "${script}"

The checkpoint form runs a command once and skips it on later runs: frobulator.complete [ continue | halt ] "[path]" "[checkpoint]" command [arguments...] - skips the command if "${path}/${checkpoint}" exists, otherwise runs it, records its status and creates the checkpoint file on success. On failure the stale checkpoint is removed; halt then prints the summary and exits.

frobulator.complete "${checkpoint_directory}" "make-all" make all

frobulator.complete halt "${checkpoint_directory}" "image-checkpoint" download_image

frobulator.halt

Progress

Stops the script when the command that ran immediately before it failed - the stop behind frobulator.complete halt, also callable directly - a one-line replacement for an if [ "${?}" -ne 0 ] block ending in exit. On success it records the status for frobulator.result and continues silently. On failure it prints the optional message with frobulator.err, writes a dated line to the script's log through frobulator.silence log (~/.local/var/log/<script>-<stamp>.log), and exits with the failed command's own status.

zipalign -p -f 4 "${file_unsigned}" "${file_aligned}"
frobulator.halt "Align failed" "[ ${file_aligned} ]"
mkdir -p "${directory}"
frobulator.halt

frobulator.fail

Progress

Same as frobulator.halt, for use inside functions: instead of exiting the script it returns the failed status, so the caller keeps control. Follow it with || return to leave the calling function - a function cannot make its caller return on its own.

build () {
	make all
	frobulator.fail "Build failed" "[ ${target} ]" || return
}

frobulator.track

Progress

One-line replacement for an if [ "${?}" -ne 0 ]; then status=1; fi block inside a function: records the failure of the command that ran immediately before it in the calling function's status, and returns that command's own exit status - so it can be chained to leave a loop or the function.

build () {
	local status=0
	make all
	frobulator.track
	make install
	frobulator.track || return 1
	return "${status}"
}

frobulator.result

Progress

Evaluates every status recorded by frobulator.complete since the last call, reports overall success or a failure count (pointing at the log directory on failure: ${HOME}/.local/var/log/, or ${PREFIX}/var/log/ for a system-context run - the same directory frobulator.silence log writes to), then clears the recorded checkpoint/status collections. Use in tandem with frobulator.complete.

frobulator.result setup

frobulator.directory

Files

Creates a directory (and sets ownership via frobulator.ownership) from a path/name or array. With a single argument containing a /, the path is split automatically (parent directory vs. final segment) — so a single absolute path works correctly here, unlike some of the item-based helpers below.

frobulator.directory "${HOME}/.config" "frobulator"
frobulator.directory "${HOME}/.config/frobulator"

frobulator.write

Files

Writes content to one or more files under path (path defaults to ${PWD} when a 3-argument call is used), creating the directory first if needed, and sets ownership on each written file. mode controls how content is applied:

  • append — adds content to the end of the file
  • write — overwrites the file entirely
  • prepend — adds content before the file's existing contents
frobulator.write append "enabled=true" "${HOME}/.config/frobulator" "config"

frobulator.file

Files

Creates one or more empty files (via touch) under path (path defaults to ${PWD} when a single-argument call is used) and sets ownership on each.

frobulator.file "${HOME}/.config/frobulator" "config"

frobulator.keep

Files

Reverse-selects: cds into path, then deletes every item in that directory except the file(s)/array you list — the inverse of frobulator.delete.

frobulator.keep "${HOME}/Downloads" "important.zip"

frobulator.delete

Files

Deletes selected item(s) from a directory. A single argument is treated as an item relative to ${PWD} (unlike frobulator.directory, it does not auto-split a /-containing single argument into path + item — pass the base path and item separately, or use dirname/basename, when deleting an absolute path). Two arguments treat the first as the base path and the second as the item or array of items. Warns instead of silently succeeding when the resolved target doesn't exist.

frobulator.delete "${temporary_directory}" "old-file.tmp"
list=( adb fastboot )
frobulator.delete "${directory_android}" list

frobulator.copy

Files

Recursively copies file(s)/directories: frobulator.copy "[source]" "[target]" "[file]" | "[array]". Creates target if missing.

frobulator.copy "${source_directory}" "${target_directory}" "config"

frobulator.move

Files

Moves file(s)/directories, then sets public execute permissions (755) on the moved item(s) at the target via frobulator.permissions. source defaults to ${PWD} per item if left empty.

frobulator.move "${source_directory}" "${target_directory}" "archive.tar.gz"

frobulator.image

Files

Displays a local or remote image directly in supported terminals (local file path, http(s):// URL, or stdin). Width defaults to terminal width and cells/percentages are accepted; specifying an explicit height disables automatic aspect-ratio preservation.

frobulator.image "${image_file}"
frobulator.image "${image_file}" 50%
frobulator.image "${image_file}" 80 40

frobulator.http

Network

Fetches the HTTP status code for a URL into ${status_url} — silently, via curl --write-out. Called with one argument it checks the URL as-is; called with two (url, data) it checks "${url}/${data}" following redirects. Use before frobulator.status.

frobulator.http "https://example.com/file.tar.gz"

frobulator.status

Network

Interprets ${status_url} (set by a prior frobulator.http call) into a 1xx/2xx/3xx/4xx/5xx category, prints a colored status line accordingly, and sets ${proceed} to 1 (ok to continue) or 0 (abort) plus ${reason} (client/server/response) on failure.

frobulator.http "https://example.com/file.tar.gz"
frobulator.status

frobulator.repository

Network

Lists the file names at the top level of a GitHub repository through its contents listing (curl only - names extracted with grep and cut). An optional prefix keeps only names starting with it. Names are stored in the frobulator_return array (reset on each call); an unreachable listing or no matching name is an error and makes the function return 1. Pair with frobulator.download to fetch the listed files.

frobulator.repository "nathaneltitane/terminal" "bash-function-"

frobulator.download get.trmnl.me "${HOME}"/.local/bin "${frobulator_return[@]}"

frobulator.download

Network

Downloads file(s), one request per item, reported by the HTTP status code of the download itself (2xx is success; any other code, or an unreachable server, is an error and makes the function return 1). Each file is written to a partial file beside the target and only moved into place on success, then set to public execute (755) via frobulator.permissions. Missing directories are created. The caller's arrays are left untouched.

Call forms:

  • "[url]" "[directory]" "[item]" | "[array]" - each item from "[url]"/"[item]" into the directory. A single item is fetched from the url itself when the url already ends with the item or with a file name (extension) - so the item becomes the saved name.
  • "[url]"/"[item]" "[directory]"/"[item]" - the url is the file, saved under the given path.
  • "[url]"/"[item]" "[directory]" - the url is the file, saved under its own name.
  • "[url]" "[source item]" "[directory]" "[item]" - "[url]"/"[source item]" saved under a different name.

Every message ends its bracket with // <status code> - 000 when no response was received. A server that does not answer within 30 seconds is reported as unreachable.

frobulator.download get.frbltr.app "${HOME}/.local/bin/frobulator"
list=( "dextop" "dextop-additions" )
frobulator.download get.dxtp.app "${HOME}/.local/bin" "${list[@]}"
frobulator.download "https://dl.winehq.org/wine/wine-mono/7.4.0/wine-mono-7.4.0-x86.msi" "${HOME}/Downloads" wine-mono.msi

frobulator.upload

Network

Uploads data or file(s), one request per item. First argument is the HTTP request method (POST, PUT, etc.), second is the URL, remaining argument(s)/array are payloads — a {...} or [...] JSON string is sent with a JSON content type, an @file argument is sent as multipart form data, anything else is sent as URL-encoded form data. Each upload runs with a frobulator.progress "upload" indicator and is reported by the HTTP status code of the upload itself (2xx is success; any other code, or an unreachable server, is an error and makes the function return 1). Every message ends its bracket with // <status code> - 000 when no response was received (unreachable server - no answer within 30 seconds - or the request was never sent). The caller's arrays are left untouched.

frobulator.upload POST "https://example.com/upload" "${archive_file}"
frobulator.upload POST "https://example.com/upload" '{"key":"value"}'
list=( "name=one" "name=two" )
frobulator.upload PUT "https://example.com/items" "${list[@]}"

frobulator.silence

Wrappers

Runs a command (given as one string, via "${SHELL}" -c) with all its output out of sight. By default stdout and stderr go to the null sink. With log first, they are appended to the script's timestamped log file instead — ${directory_log}/${script}-${stamp}.log, where directory_log is ${PREFIX}/var/log for a system-context run or ${HOME}/.local/var/log otherwise. It runs in the foreground; to show a ticker while it runs, start it through frobulator.background.

frobulator.silence "apt-get update"
frobulator.silence log "apt-get install --assume-yes curl"

frobulator.background

Wrappers

Runs a command in the background without the shell's job notices — no [1] 12345 when it starts and no [1]+ Done when it ends, even in an interactive shell where a bash function calls it. Job control is switched off while the command runs and restored afterwards; only the job start notice goes to the null sink, so the command's own errors still reach the terminal. The command is given as separate arguments after --, so paths and patterns pass through untouched.

  • With an action (and optional detail) before --, it shows a frobulator.progress ticker until the command ends and returns the command's exit status. The command's stdout goes to the null sink, since the ticker owns the line.
  • With nothing before --, the command is left running detached and its process identifier is stored in frobulator_return. Its output is left as is; redirect the whole call to silence it.

Options, in this order, before the action:

  • quiet — the command's errors go to the null sink as well.
  • process — use the frobulator.process ticker with free text instead of the frobulator.progress action verb.
  • output "[file]" — write the command's stdout to that file instead of the null sink, to read once the ticker ends.
frobulator.background download "[ ${file} ]" -- curl -s -L -o "${file}" "${url}"
frobulator.background quiet generate -- blender --background "${file_model}"
frobulator.background process "Stopping display server" -- x11vnc -R stop
frobulator.background output "${file_status}" download -- curl -s -o "${file}" -w "%{http_code}" "${url}"
frobulator.background install -- frobulator.silence log "apt-get install --assume-yes curl"
frobulator.background -- mpv --no-terminal "${file_audio}"

frobulator.password

Input

Captures masked password input character-by-character, showing • per keystroke and handling backspace/delete, until Enter. Result is stored in ${password} (and returned via ${input} as well).

frobulator.password

frobulator.input

Input

Captures one or more prompted values by label. Labels containing password are captured with frobulator.password; everything else uses a plain read. Re-prompts on empty input. Results accumulate in the frobulator_return array as label=value pairs.

options=( "username" "password" )
frobulator.input "${options[@]}"

frobulator.manager

Packages

Prints the package manager command for an action: update, upgrade, install, reinstall, purge, autoremove, autoclean, clean, hold or release. Prints nothing when the manager has no equivalent (dnf has no autoclean).

command_install="$(frobulator.manager install)"

frobulator.installed

Packages

Prints install ok installed when the package is installed - read from rpm under dnf/yum and from dpkg-query under apt.

frobulator.installed curl

frobulator.clean

Packages

Runs apt-get autoremove, autoclean, and clean in sequence (each shown with its own progress indicator), then clears stale dpkg post-install scripts under ${PREFIX}/var/lib/dpkg/info to avoid package configuration errors. Under dnf: dnf autoremove and dnf clean all.

frobulator.clean

frobulator.hold

Packages

Marks package(s) for version freeze via apt-mark hold. Under dnf: dnf versionlock add - built into dnf5 (Fedora 41 and later), a plugin on dnf4 (python3-dnf-plugin-versionlock).

frobulator.hold firefox-esr

frobulator.release

Packages

Reverses frobulator.hold via apt-mark unhold. Under dnf: dnf versionlock delete.

frobulator.release firefox-esr

frobulator.failsafe

Packages

Runs a background apt update && apt full-upgrade (dnf makecache and dnf upgrade under dnf), then sequentially updates/upgrades/installs each named package — a heavier pre-flight pass meant to avoid "not found" or "ignored" errors on the install(s) that follow.

frobulator.failsafe curl

frobulator.install

Packages

Installs package(s). A .deb or .rpm path is installed directly; otherwise the package is looked up with apt search first, and installation is skipped (with a "present on system" message) if it's already installed.

frobulator.install curl

frobulator.require

Packages

Checks whether each name is available as a command or as an installed package (dpkg-query), and if neither, searches apt-file to find and install the package that provides it. apt-file is only installed and refreshed when something is actually missing, so requirements that are already met need no root access — supports * glob package-name queries too, expanding them against apt-cache pkgnames before installing every match. Under dnf the installed check reads rpm, the providing package comes from dnf repoquery --whatprovides on /usr/bin and /usr/sbin and globs expand through dnf repoquery - no apt-file needed.

frobulator.require curl
frobulator.require "libssl*"

frobulator.reinstall

Packages

Reinstalls package(s) via apt-get install --reinstall (dnf reinstall under dnf).

frobulator.reinstall curl

frobulator.update

Packages

Runs apt-get update (dnf makecache under dnf) with a progress indicator.

frobulator.update

frobulator.upgrade

Packages

Runs apt-get dist-upgrade (dnf upgrade under dnf) with a progress indicator.

frobulator.upgrade

frobulator.purge

Packages

Purges package(s) via apt-get purge --autoremove (dnf remove under dnf), but only if apt search shows the package as currently installed (skips with a message otherwise).

frobulator.purge unused-package

frobulator.dialog

Packages

Opens a native directory-selection dialog via zenity (GNOME) or kdialog (KDE), whichever is available, with ${script} in the window title, and prints the selection (one path per line) for capture. Extra arguments are passed on to the dialog tool. Fails with a warning if neither is installed.

directory="$(frobulator.dialog "Directory")"

frobulator.terminate

System

Forcefully and repeatedly pkill -f's a process name/pattern until pgrep -f no longer finds it. Requires both pgrep and pkill.

frobulator.terminate rogue-process

frobulator.exit

System

Cleanly exits the current script or process instance — runs a 3-second frobulator.countdown ("Exiting"), then calls the exit builtin with an explicit exit code (defaults to 0, or 1 if the countdown itself was interrupted). Does not touch the shell — see frobulator.close for that behavior. Being terminal, it never returns to its caller like the rest of the library does.

frobulator.exit setup
frobulator.exit setup "${status}"

frobulator.close

System

Runs the same 3-second frobulator.countdown, then forcefully terminates the current ${SHELL} via frobulator.terminate — this ends the shell session, not just the calling script. (This is what frobulator.exit used to do before the two were split.)

frobulator.close setup

frobulator.user

System

Prints the active shell user (${SUDO_USER} if set, otherwise ${USER}) and pauses 1 second — useful before privilege-sensitive steps.

frobulator.user

frobulator.assess

System

Checks whether the listed command(s) exist. If run as root, installs any missing ones via frobulator.require and asks the person to restart as their normal user. If not root, prompts to escalate (frobulator.escalate) for any missing command, or warns and fails if declined.

requirements=( curl git )
frobulator.assess "${requirements[@]}"

frobulator.escalate

System

Relaunches the current script as root via sudo, preserving the original arguments (read back from the self_arguments array set by the bootstrap header - spaces, quotes and empty arguments are kept exactly; a plain string from older headers is still word split). If already root, resolves the correct non-root USER/HOME from SUDO_USER instead of re-launching. Before restarting it clears the bootstrap header and its own messages (3 header lines + lines_printed_terminal); the restarted run then clears its own header and the sudo password prompt, so only the escalated runtime messages stay on screen.

self_arguments=("${@}")
frobulator.escalate

frobulator.self

System

Declares frobulator as loaded in the current shell by setting frobulator_loaded="true" - the bootstrap header checks it to skip downloading and sourcing again. Runs automatically when frobulator is sourced.

frobulator.self

frobulator.service

System

Manages a systemd service, scoping automatically to --user or system context depending on whether the runtime is escalated (root). A single argument is treated as a daemon-level action with no target unit; two arguments target a specific service with the given action forwarded to systemctl. activate runs a full enable/start cycle with status verification. reload is the only action that doesn't require a service. Supported actions: reload, status, activate, enable, disable, start, stop, restart.

frobulator.service reload
frobulator.service reload "${service}"
frobulator.service activate "${service}"

frobulator.ownership

System

Restores ownership on a target after privileged operations. Resolves the real path and classifies it: paths under the invoking user's home directory are attributed to that user; paths under system directories (/root, /usr, /etc, /var, /opt, /boot) are attributed to root; anything else falls back to the invoking user. Applied recursively via chown. Called internally by frobulator.directory, frobulator.write, frobulator.file, and frobulator.link.

frobulator.ownership "${directory_android}" "${HOME}/.local/bin/adb"

frobulator.permissions

System

Sets file / directory permissions from a scope and optional modifiers, in place of raw chmod modes. Takes the scope first, then any modifiers (any order), then one or more targets (or an array). Sets the exact mode; directories always keep execute for whoever can read them, since a directory cannot be opened without it.

  • scope: public (you edit, everyone reads), group (you edit, your group reads, no access for others), private (only you)
  • modifiers: execute (whoever can read can also run), readonly (nobody edits, including you)
callfiledirectorytypical use
public644 rw-r--r--755 rwxr-xr-xregular files, configs, shared data
public execute755 rwxr-xr-x755 rwxr-xr-xscripts, binaries, installed tools
public readonly444 r--r--r--555 r-xr-xr-xreference files that should not change
public readonly execute555 r-xr-xr-x555 r-xr-xr-xlocked-down tools
group640 rw-r-----750 rwxr-x---files for your group only
group execute750 rwxr-x---750 rwxr-x---group-only scripts
group readonly440 r--r-----550 r-xr-x---group reference files
group readonly execute550 r-xr-x---550 r-xr-x---locked group tools
private600 rw-------700 rwx------credentials, keys, tokens
private execute700 rwx------700 rwx------personal scripts
private readonly400 r--------500 r-x------ssh keys, certificates
private readonly execute500 r-x------500 r-x------locked personal tools
frobulator.permissions public execute "${path}/${file}"
frobulator.permissions private "${HOME}/.git-credentials"
frobulator.permissions private readonly "${HOME}/.ssh/id_ed25519"

frobulator.archive

Archives

Creates an archive from a directory (defaults to ${PWD} when omitted). Supported type values: tar, tar.gz/tgz, tar.bz2/tbz2, zip, 7z, rar — each checked and installed via frobulator.require before use.

frobulator.archive "backup" tar.gz "${HOME}/Documents"

frobulator.extract

Archives

Extracts a known archive type (detected from everything after the first period of the file name, so names like backup-1.2.tar.gz are not recognized) into directory (defaults to ${PWD} when omitted), installing the needed extractor (tar, 7z, unrar, unzip) via frobulator.require first.

frobulator.extract "backup.tar.gz" "${target_directory}"

USES // PROJECTS BUILT ON FROBULATOR

These projects build their scripts on Frobulator.