mcpField
L
linux-bash-scripting

linux-bash-scripting

github.com/iliaalDevelopment45180 installsUpdated 3d ago
Repository

About linux-bash-scripting

A skill for writing and validating Bash scripts targeting GNU Bash 4.4+ on Linux. It gives developers concrete safety, portability-within-Linux, and performance practices, with checks for ShellCheck and shfmt before completion.

Key features

  1. 01Targets GNU Bash 4.4+ on Linux
  2. 02Applies safe input and process handling
  3. 03Covers files, secrets, and cleanup
  4. 04Uses ShellCheck and shfmt validation
  5. 05Provides syntax-check fallback guidance

Use cases

  1. 01Writing scripts for Linux automation
  2. 02Handling untrusted input and subprocesses
  3. 03Checking scripts before delivery

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/iliaal/ai-skills --skill linux-bash-scripting

After installing, type/linux-bash-scriptingin Claude Code to confirm it loaded.

Bundled files

References and scripts installed along with SKILL.md.

SPEC.md
references/
input-and-process-safety.md
production-patterns.md

Details

From the project README

Linux Bash Scripting

Produce bash scripts that pass shellcheck --enable=all and shfmt -d with zero warnings.

Target: GNU Bash 4.4+ on Linux. No macOS/BSD workarounds, no Windows paths, no POSIX-only restrictions.

Script Foundation

#!/usr/bin/env bash
set -Eeuo pipefail
shopt -s inherit_errexit

SCRIPT_DIR=$(dirname -- "${BASH_SOURCE[0]}") || exit "$?"
SCRIPT_DIR=$(cd -- "${SCRIPT_DIR}" && pwd -P) || exit "$?"
readonly SCRIPT_DIR

trap 'printf "Error at %s:%d\n" "${BASH_SOURCE[0]}" "$LINENO" >&2' ERR
trap 'rm -rf -- "${_tmpdir:-}"' EXIT
  • -E propagates ERR traps into functions
  • inherit_errexit propagates errexit into $() command substitutions
  • Resolve the script's own data files against SCRIPT_DIR, never the caller's cwd or git rev-parse --show-toplevel. A shared linter invoked from another project's git hook, a cron job, or a wrapper runs with someone else's cwd, so a caller-relative rules path resolves to a file that does not exist: the rule set loads empty, zero violations are found, exit 0. It is a silent no-op, not an error, and running it from inside its own repo passes for the wrong reason. Exercise it once from a scratch directory that is not the script's own tree
  • Always create temp dirs under the EXIT trap: _tmpdir=$(mktemp -d)
  • Wrap the body in a main function. Guard its invocation so direct execution runs it and sourcing only defines functions for testing.

Core rules

  • Quote expansions, use arrays for commands, and never evaluate external data as shell code.
  • Validate numeric syntax, sign, and application bounds before arithmetic. Convert unsigned digits with 10# before applying the sign; 10#-08 is invalid.
  • Keep secrets out of process arguments and tracing. Feed them through stdin and use a JSON-aware encoder.
  • Check exact exit statuses where “absent” differs from “failed to inspect.” Separate local and readonly declarations from command substitutions.
  • Use NUL-delimited file iteration, validate required flag values, and reject conflicting output/target combinations.
  • For atomic replacement, stage beside the destination; for multi-file activation, switch a single staged release reference.
  • Preserve unrelated files and report the actual signal or command status after cleanup.
  • Do not assume Bash options work in sh, GNU utility modes behave like syscall modes, or a configured fallback path is usable.

Task-specific references

Read the relevant reference before implementing the matching behavior:

  • For command execution, external input, numeric conversion, argument parsing, iteration, or subprocess status handling: input-and-process-safety.md.
  • For file activation, secrets, locking, retries, cleanup, permissions, logging, or restartable automation: production-patterns.md.

Performance

  • Parameter expansion over externals: ${path%/*} not dirname, ${path##*/} not basename, ${var//old/new} not sed
  • (( )) over expr; [[ =~ ]] over echo | grep
  • Cache results: val=$(cmd) once, reuse $val
  • xargs -0 -P "$(nproc)" for parallel work
  • declare -A map for lookups instead of repeated grep

Bash 4.4+ / 5.x

  • ${var@Q} shell-quoted (Bash 4.4+); ${var@U} uppercase and ${var@L} lowercase (Bash 5.1+)
  • declare -n ref=varname nameref for indirect access
  • wait -n wait for any background job
  • $EPOCHSECONDS, $EPOCHREALTIME: timestamps without forking date (Bash 5.0+)

Linux-Specific

  • GNU coreutils differ from macOS: sed -i (no '' suffix), grep -P (PCRE support), readlink -f (canonical path)
  • timeout 30s cmd to prevent automation hangs
  • sed -i replaces a symlink with a regular file: it writes a temp file and renames it over the path, so the two copies diverge later with no error. Use sed -i --follow-symlinks or edit the target by name, and check git status --short for T (typechange) after scripted edits

ShellCheck

Run shellcheck --enable=all script.sh. Key rules:

  • SC2155: Separate declaration from assignment
  • SC2086: Double-quote variables
  • SC2046: Quote command substitutions
  • SC2164: cd dir || exit
  • SC2327/SC2328: Keep stdout available to command substitution; redirecting it away leaves the captured value empty
  • Read regex captures from ${BASH_REMATCH[n]}, not positional parameters

Pre-commit: shellcheck *.sh && shfmt -i 2 -ci -d *.sh

Verify

Run shellcheck --enable=all and shfmt -d with zero warnings before declaring done. Test edge cases: empty input, missing files, spaces in paths.

If shellcheck or shfmt is not installed (command -v shellcheck fails), the check was skipped, not passed: report "static analysis not run: shellcheck unavailable" and fall back to bash -n for syntax only. A skipped linter is the same silent no-op as the empty rules file above.

Comments

Markdown supported