Skip to content
Agentmart
Submit a tool

bash-shortening

Rewrite verbose Bash into concise, idiomatic Bash without losing readability.

Skill by paulnsorensen

IndexedMIT★ 2
Write a review

Install

Works with Claude Code, Claude apps, Codex, Cursor, Gemini CLI, GitHub Copilot, VS Code, OpenCode, Goose, Windsurf. Read the repository before you give any tool access to your data.

npx agentmarket install skill/paulnsorensen-bash-shortening

Asks which of your tools to add it to (Claude Code, Codex, Cursor, VS Code, Windsurf, Gemini CLI, Claude Desktop) and shows the change first. See https://agentmart.dev/cli.

Get the weekly diff

The weekly diff: one email a week with new agent tools, notable updates and grade changes, plus security alerts for tools you follow. Double opt-in, one-click unsubscribe.

We use your email only for this newsletter. Privacy

About

Rewrite verbose Bash into concise, idiomatic Bash without losing readability. Use when the user says "shorten this script", "make this more idiomatic", "clean up this bash", "this script is too long", "is there a shorter way to do this in bash", asks for a review of a Bash script, or when you write a new Bash `.sh`/`.bash` script or `bash` block. Covers parameter expansion, brace expansion, process substitution, arithmetic, functions, heredocs, associative arrays, parallel jobs, and IFS parsing, and refuses cryptic one-liners. Do NOT use for fish, zsh, or POSIX `/bin/sh` scripts, where bashisms break portability.

Use cases: writing

No reviews yet. Installed it? Reviews come from verified installers only. How reviews work

Permissions

What this skill can do and what it asks for, from its SKILL.md. Checked Oct 25, 2026.

Tools
No tools found
Secrets
None asked for
Install scripts
None found
Runs as
Python package

Network hosts it talks to 1

  • codeload.github.com · found in install command

Files it touches

  • ~/.claude/skills/bash-shortening

What's inside

Skill name
bash-shortening
Description
Rewrite verbose Bash into concise, idiomatic Bash without losing readability. Use when the user says "shorten this script", "make this more idiomatic", "clean up this bash", "this script is too long", "is there a shorter way to do this in bash", asks for a review of a Bash script, or when you write a new Bash `.sh`/`.bash` script or `bash` block. Covers parameter expansion, brace expansion, process substitution, arithmetic, functions, heredocs, associative arrays, parallel jobs, and IFS parsing, and refuses cryptic one-liners. Do NOT use for fish, zsh, or POSIX `/bin/sh` scripts, where bashisms break portability.
License
MIT
Instructions
153 lines in SKILL.md · view on GitHub

Before installing

  • Ships executable scripts

Files (22)

  • AGENTS.md
  • SKILL.md
  • advanced.md · references/
  • anti-patterns.md · references/
  • arithmetic.md · references/
  • brace-expansion.md · references/
  • command-substitution.md · references/
  • functions.md · references/
  • parameter-expansion.md · references/
  • process-substitution.md · references/
  • real-world.md · references/
  • bash-shorten.py · scripts/
  • engine.py · scripts/
  • rules.py · scripts/
  • backticks.yml · scripts/sg-rules/
  • test-numeric-eq.yml · scripts/sg-rules/
  • test-numeric-ge.yml · scripts/sg-rules/
  • test-numeric-gt.yml · scripts/sg-rules/
  • test-numeric-le.yml · scripts/sg-rules/
  • test-numeric-lt.yml · scripts/sg-rules/
  • test-numeric-ne.yml · scripts/sg-rules/
  • sgconfig.yml · scripts/

SKILL.md (first lines)

# bash-shortening

Make Bash scripts shorter without making them harder to read.
The source is 51 techniques from Karandeep Singh's "Bash Code Shortening"
article, kept as a cheatsheet plus categorized references.

## Philosophy

Shortening expresses intent; it does not save keystrokes.
The win is fewer moving parts: subprocesses, temp files, and intermediate variables.
A cryptic `${1:-${X:-${Y/-,/ }}}` chain is worse than three clear lines.

Two questions decide every change:

1. Does the shorter form remove a class of bugs (a forgotten `rm`, a missed branch, a check-then-`mkdir` race)? If yes, shorten.
2. Does the shorter form hide intent from the next reader? If yes, keep it long.

When both fire, prefer clarity.

## Flow

Analyzers gather in parallel; the main thread is the only writer.
Parallel writes to one file race, and category rewrites overlap on the same lines.

1. **Identify the target.** Take file paths, a pasted snippet, or a `bash` block.
   Done when the shebang or context confirms Bash (see "When not to use").
2. **Run the analyzer sweep.** Dispatch one read-only analyzer per row of the category table, all in one batched call.
   Use any parallel primitive the host has: sub-agents, parallel tool calls, or `parallel`/`xargs -P`.
   With no parallel primitive, walk the table in one pass and tick off each row.
   Done when every category has a report, including empty ones.
3. **Merge.** Collect the `path:line — verbose → idiomatic` hits.
   Keep one entry per line and note every category that flagged it.
4. **Run the rewriter.** From the main thread, run `python3 scripts/bash-shorten.py --apply <file>` once per file.
   Paths under `scripts/` and `references/` are relative to this skill's directory.
   Never give `--apply` to an analyzer.
5. **Verify.** Run `bash -n <file>`, then `shellcheck <file>` when present, then read the diff.
   When a rule gives a surprising result, revert and narrow with `--rules` or `--skip`.
   Done when syntax and shellcheck pass and every hunk is expected.
6. **Show the punch list.** Present the analyzer hits the rewriter did not cover.
   Let the user pick categories, especially where anti-pattern hits conflict with shortening hits.
7. **Hand-edit.** Apply one technique per change, with its rationale visible.
   Use the before/after examples in the matching reference.
   Keep quoting, `set -euo pipefail`, and error handling.
   Stop before a rewrite needs a comment to explain *what* it does.

**Analyzer brief** (substitute `<category>` and `<target>`):

> Read `references/<category>.md` in the bash-shortening skill directory.
> Scan `<target>` for every pattern it covers.
> Return a markdown list: `path:line — verbose form → idiomatic form`.
> Do not rewrite or write any file, and do not run `scripts/bash-shorten.py`.
> Flag a hit that conflicts with the anti-patterns reference, but still include it.

## Categories

This table is the complete checklist.
A single reviewer tunnels on the first two or three categories; the sweep exists to prevent that.
The article numbers map to the example numbers inside each reference.

| Reference | Article | Patterns to find |
|---|---|---|
| `references/command-substitution.md` | 3-7 | temp files replaced by `$(cmd)` or pipelines, reused output, `xargs`, `find \| xargs` vs process substitution |
| `references/parameter-expansion.md` | 8-15 | `if [ -z "$X" ]` defaults, `cut -c`/`echo \| sed` substitutions, `${#S}`, `basename`/`dirname` |
| `references/functions.md` | 16-20 | repeated logging blocks, default params, echo-returns, named params |
| `references/brace-expansion.md` | 21-26 | sequential `mkdir`/`touch`, literal `for i in 1 2 3` ranges, zero-padded sequences |
| `references/process-substitution.md` | 27-31 | temp files feeding `diff` or loops, `echo x \| cmd` → `<<<` |
| `references/arithmetic.md` | 32-38 | `expr`, `$(…)+1` increments, `[ -gt ]` numeric tests, `[ A ] && [ B ]`, ternary gotcha |
| `references/real-world.md` | 39-45 | config parsing, log analysis, health checks, batch jobs, backups, API + `jq` |
| `references/advanced.md` | 48-51 | repeated `echo` → heredoc, 3+ branch `if` → `case` or assoc array, independent commands → `& wait`, `cut -d,` in loops → `IFS` |
| `references/anti-patterns.md` | 1-2, 46-47 | nested expansions, cryptic one-liners, places where shortening hurts; flag only, never auto-rewrite |

## Rewriter

`scripts/bash-shorten.py` applies the high-confidence rewrites.
It needs no third-party Python packages, but it requires [ast-grep](https://ast-grep.github.io/) (`sg`) on PATH.
It is a dry run by default: it prints a unified diff and per-rule counts.

```bash
python3 scripts/bash-shorten.py script.sh                         # preview
python3 scripts/bash-shorten.py --apply script.sh                 # atomic in-place write
python3 scripts/bash-shorten.py --rules backticks,test-numeric script.sh
python3 scripts/bash-shorten.py --skip find-exec-rm-delete script.sh
python3 scripts/bash-shorten.py --include modernize --apply script.sh
python3 scripts/bash-shorten.py --list                            # rules, groups, article refs
python3 scripts/bash-shorten.py --explain test-numeric
python3 scripts/bash-shorten.py --self-test
```

- **`core`** (default): idiomatic rewrites that keep the same tools.
- **`modernize`** (opt-in): rewrites to `sd`, `rg -F`, and `fd`.
  They need those binaries, and `find → fd` changes behavior because `fd` respects `.gitignore`.
- **Opt-out:** a `# bash-shorten: disable` … `# bash-shorten: enable` span, or `# bash-shorten: skip` for the next line, protects code from every rule.

The rewriter cannot do data-flow work: single-use variable inlining, temp-file → pipeline, function extraction, parallelization, or judging whether an `&& … ||` chain is safe.
Those are hand-edits from the references.

When `sg` is missing, the script exits with a diagnostic.
Install it (`brew install ast-grep` or `cargo install ast-grep --bin sg`), or skip the rewriter and run only the analyzer sweep.
To add or change a rule, read `AGENTS.md` first.

## Quick wins

| Verbose form | Idiomatic form | Reference (article #) |
|---|---|---|
| `if [ -z "$X" ]; then X=default; fi` | `X=${X:-default}` | parameter-expansion (8) |
| `$(echo "$S" \| cut -c1-5)` | `${S:0:5}` | parameter-expansion (10) |
| `$(basename "$P")` / `$(dirname "$P")` | `${P##*/}` / `${P%/*}` — hand-edit only: both break on a trailing `/`, and `dirname` returns `.` for a path with no `/` | parameter-expansion (11-12) |
| `$(echo "$S" \| sed 's/a/b/g')` | `${S//a/b}` | parameter-expansion (13-14) |
| `$(echo -n "$S" \| wc -c)` | `${#S}` | parameter-expansion (15) |
| `$(expr $A + $B)` / `C=$(expr $C + 1)` | `$((A + B))` / `((C+=1))` | arithmetic (32-34) |
| `[ $X -gt 100 ]` / `[ $A ] && [ $B ]` | `((X > 100))` / `[[ $A && $B ]]` | arithmetic (35, 37) |
| `mkdir a; mkdir b; mkdir c` | `mkdir -p {a,b,c}` | brace-expansion (21-22) |
| `for i in 1 2 3 4 5` | `for i in {1..5}` (or `{01..10}`, `{2..10..2}`) | brace-expansion (23-26) |
| `cmd > /tmp/x; cmd2 < /tmp/x; rm /tmp/x` | `cmd \| cmd2` or `cmd2 < <(cmd)` — differ from the temp-file form in concurrency, `SIGPIPE` handling, and `pipefail` exit status | command-substitution (5), process-substitution (29) |
| `sort a > /tmp/a; sort b > /tmp/b; diff …` | `diff <(sort a) <(sort b)` | process-substitution (27) |
| `if [ "$E" = dev ]; elif …` (3+ branches) | `case` or `${URLS[$E]:-default}` | functions, advanced (49) |
| Repeated `echo "[$(date)] [LEVEL] msg"` | a `log()` function with `${1^^}` | functions (16) |
| `find … > /tmp/x; while read …; done < /tmp/x` | `done < <(find …)` or `find … \| xargs cmd` | command-substitution (6), process-substitution (29) |
| Many `echo "..."` lines | `cat <<EOF … EOF` | advanced (48) |

From paulnsorensen/dotfiles, MIT.

More from paulnsorensen/dotfiles

Signals

Stars
2
Forks
0
Open issues
69
Last commit (this folder)
Sep 24, 2026 (1 month ago)

From GitHub (fetched Oct 24, 2026).

Details

License
MIT
First seen
Oct 24, 2026
Last checked
Oct 24, 2026

More from paulnsorensen

Cite this page

bash-shortening skill. Agent Mart. https://agentmart.dev/skills/paulnsorensen-bash-shortening (last updated 2026-10-24)