Bash Golf Part 4

Published at 2025-09-13T12:04:03+03:00

This is the fourth blog post about my Bash Golf series. This series is random Bash tips, tricks, and weirdnesses I have encountered over time.

2021-11-29 Bash Golf Part 1

2022-01-01 Bash Golf Part 2

2023-12-10 Bash Golf Part 3

2025-09-14 Bash Golf Part 4 (You are currently reading this)

Table of Contents

Split pipelines with tee + process substitution

Sometimes you want to fan out one stream to multiple consumers and still continue the original pipeline. `tee` plus process substitution does exactly that:

All of `command1`, `command2`, and `command3` see the output of `somecommand`. Example:

Output:

This relies on Bash process substitution (`>(...)`). Make sure your shell is Bash and not a POSIX `/bin/sh`.

Example (fails under `dash`/POSIX sh):

Combine with `set -o pipefail` if failures in side branches should fail the whole pipeline.

Example:

Further reading:

Splitting pipelines with tee

Heredocs for remote sessions (and their gotchas)

Heredocs are great to send multiple commands over SSH in a readable way:

Tips:

Quoting the delimiter changes interpolation. Use `<<'EOF'` to avoid local expansion and send the content literally.

Example:

Prefer explicit quoting for variables (as above) to avoid surprises. Example (spaces preserved only when quoted):

Consider `set -euo pipefail` at the top of the remote block for stricter error handling. Example:

Indent-friendly variant: use a dash to strip leading tabs in the body:

Further reading:

Heredoc headaches and fixes

Namespacing and dynamic dispatch with `::`

You can emulate simple namespacing by encoding hierarchy in function names. One neat pattern is pseudo-inheritance via a tiny `super` helper that maps `pkg::lang::action` to a `pkg::base::action` default.

Output:

Indirect references with namerefs

`declare -n` creates a name reference — a variable that points to another variable. It’s cleaner than `eval` for indirection:

Output:

Namerefs are local to functions when declared with `local -n`. Requires Bash ≥4.3.

You can also construct the target name dynamically:

Output:

Function declaration forms

All of these work in Bash, but only the first one is POSIX-ish:

Recommendation: prefer `name() { ... }` for portability and consistency.

Chaining function calls in conditionals

Functions return a status like commands. You can short-circuit them in conditionals:

You can also compress it golf-style:

Grep, sed, awk quickies

Word match and context: `grep -w word file`; with context: `grep -C3 foo file` (same as `-A3 -B3`). Example:

Output:

Skip a directory while recursing: `grep -R --exclude-dir=foo 'bar' /path`. Example:

Output:

Insert lines with sed: `sed -e '1isomething' -e '3isomething' file`. Example:

Output:

Drop last column with awk: `awk 'NF{NF-=1};1' file`. Example:

Output:

Safe xargs with NULs

Avoid breaking on spaces/newlines by pairing `find -print0` with `xargs -0`:

Example with spaces and NULs only:

Output:

Efficient file-to-variable and arrays

Read a whole file into a variable without spawning `cat`:

Read lines into an array safely with `mapfile` (aka `readarray`):

Assign formatted strings without a subshell using `printf -v`:

Output:

Read NUL-delimited data (pairs well with `-print0`):

Quick password generator

Pure Bash with `/dev/urandom`:

Alternative using `openssl`:

`yes` for automation

`yes` streams a string repeatedly; handy for feeding interactive commands or quick load generation:

Forcing `true` to fail (and vice versa)

You can shadow builtins with functions:

To disable a builtin entirely: `enable -n true` (re-enable with `enable true`).

Further reading:

Force true to return false

Restricted Bash

`bash -r` (or `rbash`) starts a restricted shell that limits potentially dangerous actions, for example:

It’s a coarse sandbox for highly constrained shells; read `man bash` (RESTRICTED SHELL) for details and caveats.

Example session:

Useless use of cat (and when it’s ok)

Avoid the extra process if a command already reads files or `STDIN`:

But for interactive composition, or when you truly need to concatenate multiple sources into a single stream, `cat` is fine, as you may think, "First I need the content, then I do X." Changing the "useless use of cat" in retrospect is really a waste of time for one-time interactive use:

From notes: “Good for interactivity; Useless use of cat” — use judgment.

Atomic locking with `mkdir`

Portable advisory locks can be emulated with `mkdir` because it’s atomic:

This works well on Linux. Remove the lock in `trap` so crashes don’t leave stale locks.

Smarter globs and faster find-exec

Example for extglob (exclude two dirs from listing):

E-Mail your comments to `paul@nospam.buetow.org` :-)

Other related posts are:

2025-09-14 Bash Golf Part 4 (You are currently reading this)

2023-12-10 Bash Golf Part 3

2022-01-01 Bash Golf Part 2

2021-11-29 Bash Golf Part 1

2021-06-05 Gemtexter - One Bash script to rule it all

2021-05-16 Personal Bash coding style guide

Back to the main site

Proxied content from gemini://foo.zone/gemfeed/2025-09-14-bash-golf-part-4.gmi (external content)

Gemini request details:

Original URL
gemini://foo.zone/gemfeed/2025-09-14-bash-golf-part-4.gmi
Status code
Success
Meta
text/gemini;
Proxied by
kineto

Be advised that no attempt was made to verify the remote SSL certificate.