| name | command-prompt |
| description | · Write/debug shell commands, scripts, dotfiles, completions for zsh, bash, POSIX sh, fish. Triggers: 'shell', 'script', '.zshrc', '.bashrc', 'alias', 'completion', 'trap'. Not for CI blocks (use ci-cd).
|
| license | MIT |
| compatibility | Requires a POSIX-compatible shell. Zsh, bash, fish, or nushell for shell-specific features |
| metadata | {"source":"iuliandita/skills","date_added":"2026-03-25","effort":"medium","argument_hint":"<task-or-shell>"} |
Command Prompt: Shell Scripting and Configuration
Reference skill for writing commands, scripts, and configuration across Unix shells. Detects
the target shell from context and routes to the appropriate reference.
Target versions (July 2026):
- Zsh: 5.10
- Bash: 5.3
- Fish: 4.8.1
- Nushell: 0.114.1
- Tcsh: 6.24
- Dash: 0.5.13
When to use
- Writing shell commands, scripts, or one-liners
- Configuring dotfiles (
.zshrc, .bashrc, .profile, config.fish)
- Writing completions, shell functions, or aliases
- Porting scripts between shells
- Debugging shell-specific behavior (globbing, arrays, expansion, quoting)
- Setting up oh-my-zsh, starship, p10k, or other shell frameworks
- Choosing which shell to target for a new script
- Writing interactive commands on the user's local machine (zsh)
When NOT to use
- Remote FreeBSD/OPNsense/pfSense commands - use firewall-appliance (handles tcsh/csh in the BSD context)
- Ansible shell/command modules - use ansible (module gotchas differ from raw shell)
- CI/CD pipeline shell blocks - use ci-cd (restricted environments, no interactive features)
- General Linux sysadmin that isn't shell-specific - just do the task directly
AI Self-Check
Before returning any generated shell script or command, verify:
Performance
- Use builtins and stream processing for large inputs; avoid command substitution that buffers entire files.
- Prefer
rg, fd, and targeted file lists when available, with portable fallbacks noted.
- Avoid spawning subshells inside tight loops when
xargs, arrays, or shell builtins fit.
Best Practices
- Default to
set -euo pipefail only when the script is written to handle those semantics.
- Use
-- before user-controlled paths for commands that support it.
- Preview destructive expansions before
rm, mv, chmod, chown, or recursive edits.
Workflow
Step 1: Detect the target shell
Before writing any shell code, determine the target shell. Check these signals in order:
| Signal | How to check | Routes to |
|---|
| Shebang | First line of existing script | #!/usr/bin/env zsh -> zsh, #!/usr/bin/env bash -> bash, #!/bin/sh -> posix-sh |
| File name/extension | .zsh, .zshrc, .zprofile, .zshenv -> zsh; .bash, .bashrc, .bash_profile -> bash; .fish, config.fish -> fish | |
| User's shell | Conversation context, $SHELL | User's local machine = zsh |
| Task type | What the script does | See routing below |
Task-based routing
| Task | Target shell | Why |
|---|
| Interactive commands on user's machine | zsh | User's default shell |
| Portable scripts (new) | bash | Widest deployment, good feature set |
| Docker/CI containers | bash or sh | Containers often lack zsh |
| Minimal Alpine/BusyBox scripts | POSIX sh | Only ash/dash available |
| BSD system administration | tcsh | FreeBSD default (but see firewall-appliance skill) |
| Cross-shell startup (env vars, PATH) | POSIX sh | .profile sourced by all POSIX shells |
| Maximum portability requirement | POSIX sh | Only standard guaranteed on all Unixes |
Step 2: Load the right reference
| Target shell | Reference file |
|---|
| Zsh | references/zsh.md (~680 lines, 14 sections) |
| Bash | references/bash.md (~710 lines, 13 sections) |
| POSIX sh | references/posix-sh.md (~490 lines, 10 sections) |
| Fish, tcsh, nushell, others | references/alt-shells.md (~420 lines, 4 shells) |
Don't load all references. Pick the one that matches. If porting between two shells, load both.
Step 3: Write code, then verify
Use the cross-shell comparison below for quick lookups. After writing, run through the
Verification Checklist at the bottom of this section.
Quick Cross-Shell Comparison
| Feature | POSIX sh | Bash | Zsh | Fish |
|---|
| Arrays | no (use $@) | 0-indexed | 1-indexed | lists (1-indexed) |
| Assoc arrays | no | declare -A (4.0+) | typeset -A | no |
Glob **/ | no | shopt -s globstar | built-in | built-in |
| Failed glob | passes literal | passes literal | error | no match |
[[ ]] | no | yes | yes | no (use test) |
Process sub <() | no | yes | yes + =() | (command | psub) |
| Word splitting | on unquoted $var | on unquoted $var | no | no |
| Arithmetic | $(( )) only | $(( )), (( )), let | $(( )), (( )) | math |
| String lowercase | - | ${var,,} | ${var:l} | string lower |
| Completions | none | basic (bash-completion) | powerful (compsys) | powerful (built-in) |
| Config file | .profile | .bashrc | .zshrc | config.fish |
| Shebang | #!/bin/sh | #!/usr/bin/env bash | #!/usr/bin/env zsh | #!/usr/bin/env fish |
Universal Patterns (All POSIX Shells)
These work in sh, bash, and zsh. Fish has different syntax for most of these - see the
alt-shells reference.
Piping and redirection
| Pattern | Effect |
|---|
cmd1 | cmd2 | Pipe stdout of cmd1 to stdin of cmd2 |
cmd > file | Redirect stdout to file (overwrite) |
cmd >> file | Redirect stdout to file (append) |
cmd 2> file | Redirect stderr to file |
cmd &> file | Redirect both stdout and stderr (bash/zsh, not POSIX) |
cmd 2>&1 | Redirect stderr to stdout |
cmd > /dev/null 2>&1 | Silence all output (POSIX-portable) |
cmd < file | Feed file as stdin |
cmd <<'EOF' | Here document (single-quoted delimiter = no expansion) |
cmd <<< "string" | Here string (bash/zsh, not POSIX) |
cmd1 | tee file | cmd2 | Send stdout to both file and cmd2 |
Chaining
| Pattern | Behavior |
|---|
cmd1 ; cmd2 | Run sequentially, ignore exit codes |
cmd1 && cmd2 | Run cmd2 only if cmd1 succeeds (exit 0) |
cmd1 || cmd2 | Run cmd2 only if cmd1 fails (exit non-0) |
cmd & | Run in background |
cmd1 && cmd2 || cmd3 | Poor man's if/else (not reliable - cmd3 runs if cmd2 fails too) |
Job control
| Command | Effect |
|---|
Ctrl+Z | Suspend foreground job |
bg / bg %N | Resume job in background |
fg / fg %N | Resume job in foreground |
jobs | List background jobs |
kill %N | Kill job by number |
wait | Wait for all background jobs |
wait $PID | Wait for specific PID |
disown %N | Detach job from shell (survives logout) |
Signals and traps
cleanup() {
rm -f "$tmpfile"
}
trap cleanup EXIT INT TERM
trap '' HUP
kill_gracefully() {
local pid=$1 timeout=${2:-5}
kill -TERM "$pid" 2>/dev/null || return
local i=0
while kill -0 "$pid" 2>/dev/null && [ $i -lt $timeout ]; do
sleep 1; i=$((i+1))
done
kill -0 "$pid" 2>/dev/null && kill -KILL "$pid"
}
Interactive "kill by name" (zsh) - covers search, space-safe names, confirm, TERM->KILL escalation:
pk() {
local pattern=$1 pids
pids=(${(f)"$(pgrep -af -- "$pattern")"})
(( $#pids )) || { print -u2 "no match"; return 1 }
printf '%s\n' "${pids[@]}"
read -q "?kill these? [y/N] " || { print; return 1 }
print
for line in $pids; do kill_gracefully ${line%% *} 3; done
}
Quoting rules
| Syntax | Expansion | Use for |
|---|
"double" | $var, $(cmd), ${param} expand; \ escapes | Most strings with variables |
'single' | Nothing expands, completely literal | Regexes, JSON, strings with $ or ! |
$'ansi' | \n, \t, \' interpreted (bash/zsh) | Strings needing literal control chars |
\char | Escapes one character | Single special chars in unquoted context |
Golden rule: when in doubt, double-quote. "$var" is almost always correct. Unquoted $var
causes word splitting (in sh/bash) or glob expansion.
Exit codes
| Code | Meaning |
|---|
| 0 | Success |
| 1 | General error |
| 2 | Misuse of shell builtin |
| 126 | Command found but not executable |
| 127 | Command not found |
| 128+N | Killed by signal N (e.g., 130 = Ctrl+C / SIGINT) |
Common portable idioms
command -v git >/dev/null 2>&1 || { echo "git required" >&2; exit 1; }
: "${VAR:=default}"
name="${1:-anonymous}"
tmpfile=$(mktemp) || exit 1
trap 'rm -f "$tmpfile"' EXIT
while IFS= read -r line; do
printf '%s\n' "$line"
done < file.txt
for f in *.txt; do
[ -e "$f" ] || continue
echo "$f"
done
Completions Quick Reference (Zsh)
Zsh's completion system (compsys) handles subcommand routing natively. Minimal working
example for a CLI tool with subcommands:
_mycli() {
local -a subcmds=(
'init:Initialize a new project'
'build:Build the project'
'deploy:Deploy to target environment'
)
_arguments -C \
'(-h --help)'{-h,--help}'[Show help]' \
'1:command:->subcmd' \
'*::arg:->args'
case $state in
subcmd) _describe 'command' subcmds ;;
args)
case $words[1] in
deploy) _arguments '--env[Target environment]:env:(dev staging prod)' ;;
esac
;;
esac
}
Place in a file named _mycli on your fpath, then ensure the directory is registered:
fpath=(~/.zsh/completions $fpath)
autoload -Uz compinit && compinit
Or source inline with compdef _mycli mycli (no fpath needed). The reference files have
deeper coverage: glob-qualified completions, _files, _hosts, _values, and async
completion patterns.
Verification Checklist
Before returning any shell script, check:
Reference Files
references/zsh.md - Zsh 5.9/5.10 patterns, glob qualifiers, arrays, parameter expansion, completions, autoloading, dotfile config, prompt hooks, zsh-only features, 5.10 additions (non-forking ${ }, namerefs, SRANDOM), bash porting matrix
references/bash.md - Bash 5.3 patterns, parameter expansion, arrays, conditionals, process substitution, error handling, traps, heredocs, coprocesses, bash 5.x features (non-forking ${ cmd; }, GLOBSORT, SRANDOM), script template
references/posix-sh.md - Portable POSIX sh patterns, what's POSIX and what's not, bashism avoidance checklist, which-sh-am-I, arithmetic, parameter expansion, portable conditionals
references/alt-shells.md - Fish 4.8 (syntax, functions, completions, config), tcsh/csh 6.24 (syntax, when you'll encounter it), Nushell 0.114 (structured pipelines, types), Elvish/Oils (brief)
references/ssh-tmux-autostart.md - safe shell startup pattern for interactive SSH sessions that attach to tmux without breaking non-interactive commands
Output Contract
See references/output-contract.md for the full contract.
- Skill name: COMMAND-PROMPT
- Deliverable bucket:
audits
- Mode: conditional. When invoked to analyze, review, audit, or improve existing repo content, emit the full contract - boxed inline header, body summary inline plus per-finding detail in the deliverable file, boxed conclusion, conclusion table - and write the deliverable to
docs/local/audits/command-prompt/<YYYY-MM-DD>-<slug>.md. When invoked to write a script, dotfile, or completion / answer a question / teach a concept, respond freely: deliver the artifact or explanation inline without the contract, deliverable file, or conclusion table.
- Severity scale:
P0 | P1 | P2 | P3 | info (see shared contract; only used in audit/review mode).
Related Skills
- firewall-appliance - OPNsense/pfSense uses tcsh/csh on FreeBSD. That skill handles the BSD firewall context; this skill covers tcsh syntax in general.
- ansible - Ansible
shell/command modules have their own idiosyncrasies beyond raw shell scripting. Use ansible for playbook work.
- ci-cd - CI shell blocks run in restricted environments (no interactive features, possibly no bash). Use ci-cd for pipeline design; use this skill for the shell syntax within them.
- networking - Linux network configuration (interfaces, routes, firewalls, DNS). Use networking for service and protocol administration; use this skill for the shell scripts that wrap or automate those tasks.
- debian-ubuntu - Debian/Ubuntu system administration (packages, services, cloud-init). Use debian-ubuntu for distro-level operations; use this skill for the shell scripting patterns within those tasks.
- rhel-fedora - RHEL/Fedora system administration (dnf, systemd, SELinux, subscription-manager). Use rhel-fedora for distro-level operations; use this skill for the shell scripting patterns within those tasks.
Rules
- Detect the shell first. Check shebang, file extension, or ask. Don't assume bash when the user might mean zsh.
- Load the right reference. Don't wing zsh arrays or bash parameter expansion from memory - the subtle differences justify loading the reference every time.
- Shebang is
#!/usr/bin/env <shell>. Not #!/bin/bash. The env form is portable across distros. Exception: #!/bin/sh for POSIX scripts (this IS the standard form).
set -euo pipefail in every bash/zsh script. No exceptions for scripts beyond a one-liner.
- User's interactive shell is zsh. When writing commands for the user to run locally, use zsh syntax. Bash for scripts and remote machines unless the script specifically needs zsh.
- Don't mix shell syntaxes. A bash script uses bash idioms. A zsh script uses zsh idioms. "Works in both" compromises use neither well and confuse readers.
- Quote your variables.
"$var" is the default. Unquoted $var is the exception that needs justification.