필사 모드: Modern Bash & Shell Scripting 2026 Complete Guide - Bash 5.2 · Zsh 5.9 · fish 4 · Nushell · Murex · ShellCheck · Bashly · just Deep Dive
EnglishPrologue — Why talk about the shell again in 2026
June 1989. Brian Fox released the first version of the Bourne-Again SHell as part of the GNU Project. 37 years later, in May 2026, we still tap on the shell every day. Even in an age when AI writes our code, `cd`, `ls`, `grep`, `git`, `kubectl`, and `ssh` live on as muscle memory in our fingers.
But the landscape has clearly shifted.
- **Bash 5.2** (released September 2022) is still the GNU standard, and 5.3 is due to arrive within 2026. Chet Ramey has maintained it since the early 1990s.
- **Zsh 5.9** has been the macOS default since Catalina, and it commands a massive plugin ecosystem of Oh My Zsh, Prezto, and Antigen.
- **fish 4** was fully rewritten in Rust in 2024. Start-up time, syntax highlighting, and autosuggestion all got faster and lighter on memory.
- **Nushell 0.105+** shakes the very definition of a shell. Every command emits and consumes structured data (a table), and you write SQL-flavored pipelines like `ls | where size > 1mb | sort-by modified`.
- **Murex** — a shell with a type system. **xonsh** — a shell scripted in Python. **Elvish** — an expression-rich shell. All of them have carved out their own niche.
- And then the tooling. **ShellCheck** is the ESLint for shell scripts. **shfmt** is the formatter. **bashly** generates full-stack CLI apps from a YAML spec. **gum** lets shell scripts paint pretty TUIs. **fzf**, **ripgrep**, **fd**, **eza**, **dust**, **bottom**, **just**, **mise** — every one of them is a next-generation rewrite of GNU coreutils in Rust or Go.
- AI showed up too. **shell_gpt (sgpt)** turns natural language into shell commands, **Warp Terminal** floats AI suggestions, and **Amazon Q Developer** (formerly Fig) sharpens completion.
This article walks through those tools and patterns across 22 chapters. The shell itself (1–6), scripting best practices (7–12), modern CLI tools (13–17), build/task runners (18–19), AI and terminals (20), Korea/Japan ops culture (21), and finally the future (22).
> The shell isn't going away. It's just that the shell is no longer merely an input line. We've moved from the era of "shell vs IDE" to the era of "structured shell + modern tools + AI."
Chapter 1 · Bash 5.2 — the standard, from Brian Fox to Chet Ramey
Bash is the GNU project's reference shell. Brian Fox wrote the first version in 1989, and since 1992 Chet Ramey (at Case Western Reserve University) has been almost the sole maintainer. As of May 2026 the stable version is **5.2.37** (patch release November 2025), with 5.3 announced for later in the year.
**What landed in Bash 5.2**
- `BASH_REMATCH` is more reliable across regex matches.
- The `globskipdots` shell option drops `.` and `..` from `*` expansion, killing a long-running gotcha.
- `varredir_close` closes file descriptors safely when process substitution ends.
- `${var@k}` is a new parameter transformation that pulls out keys.
- `wait -p VAR` stores the resulting PID into a variable.
- Function tracing improvements make `set -T`/`trap ... DEBUG` more accurate.
**What's coming in Bash 5.3**
- `BASH_TRAPSIG` exposes which signal the trap handled.
- A curses-like mode is under discussion.
- Additional POSIX compatibility hardening.
**Why are we still using Bash?**
Three answers. 1) **Ubiquity** — Linux, macOS, WSL, BusyBox, Docker's `alpine`, almost everywhere. 2) **CI/CD baseline** — GitHub Actions, GitLab CI, Jenkins all default to bash. 3) **40 years of material** — `man bash`, Stack Overflow, the ABS Guide — the cumulative knowledge dwarfs every alternative.
Bash 5.2 features in action
shopt -s globskipdots # * no longer matches . or ..
echo *
wait -p captures the background PID
sleep 5 &
wait -p PID $!
echo "the last background PID was $PID"
New parameter transformation
declare -A map=([a]=1 [b]=2)
echo "${map[@]@k}" # list keys
**The macOS trap — Bash 3.2.57**
Here lies a major trap. The `/bin/bash` on macOS is **Bash 3.2.57, frozen since 2007** because Apple refuses GPLv3. Anything 4.0+ (`declare -A`, `mapfile`, `readarray`, `${var,,}` lowercasing, `**` globstar) doesn't work. The fix: `brew install bash` to get 5.2, and **never use `#!/bin/bash`** — that grabs the 3.2 one. Use `#!/usr/bin/env bash`.
Chapter 2 · Zsh 5.9 — the macOS default, home of Oh My Zsh
Paul Falstad (Princeton) kicked off Zsh in 1990. The current stable in May 2026 is 5.9, with 5.10 imminent. Since Apple switched the macOS Catalina default shell from Bash 3.2 to Zsh 5.7 in 2019, **more than half of developer laptops worldwide now run Zsh** (Stack Overflow Developer Survey 2025).
**What sets Zsh apart**
- **Global aliases** — `alias -g G='| grep'` lets you write `cat foo G bar` anywhere.
- **Extended globbing** — `**/*.go(.)` matches only regular files, `*(om[1,10])` the 10 most recent.
- **Associative arrays** are built in (Bash needs 4.0+).
- **Multi-fanout redirection** — `cmd > >(tee a) > >(tee b)`.
- **Rich prompt system** — `PROMPT_SUBST` for dynamic prompts.
- **An overwhelming completion system** — `compinit` knows nearly every command's options.
Zsh extended globbing
setopt EXTENDED_GLOB
ls **/*.log~*backup* # every .log except backups
ls *(om[1,10]) # ten most recent files
ls *(/^F) # empty directories only
**Oh My Zsh — the largest framework**
Robby Russell started it in 2009. As of 2026 it has 170k GitHub stars. 200+ themes, 300+ plugins. `git`, `kubectl`, `aws`, `docker`, `node`, `python` plugins flip on completion and aliases with one line.
~/.zshrc example
plugins=(git kubectl aws docker fzf z)
ZSH_THEME="agnoster"
source $ZSH/oh-my-zsh.sh
**Prezto / Zinit / Antidote — faster alternatives**
Oh My Zsh is rich but can slow start-up. Faster options:
- **Prezto** — Sorin Ionescu, 2012. Modular, lightweight.
- **Zinit** — turbo mode asynchronously loads plugins, start-up around 50 ms.
- **Antidote** — newer generation, plain-text plugin file.
Zinit turbo mode — async loading so the prompt isn't blocked
zinit wait lucid for \
OMZP::git \
OMZP::kubectl \
zsh-users/zsh-autosuggestions \
zdharma-continuum/fast-syntax-highlighting
**Zsh's downsides**
It is not 100% POSIX compatible. Some scripts behave differently from bash. So either annotate `#!/usr/bin/env zsh` explicitly or keep shared scripts in bash and use zsh only interactively — a common split.
Chapter 3 · fish 4 — the user-friendly shell, reborn in Rust
fish stands for "friendly interactive shell." Axel Liljencrantz launched it in 2005, and the 4.0 release in 2024 was a **complete rewrite from C++ to Rust**. May 2026 stable is 4.1, with start-up around 30–50 ms.
**fish's signature — sensible defaults out of the box**
- **Autosuggestion** — history-based suggestions are always on, displayed in gray and accepted with →.
- **Syntax highlighting** — red (broken command), green (valid), yellow (quoted) updates as you type.
- **Abbreviations** — stronger than aliases. They expand at input time, so history holds the real command.
- **Rich option completion** — it parses `man` pages to learn options.
- **Web-based config** — `fish_config` opens a browser theme picker.
fish syntax differs from bash. Variables, functions, and if are all clearer.
set -gx PATH $HOME/bin $PATH
function gco --description "git checkout"
git checkout $argv
end
Abbreviations: expand at input time
abbr -a gc git commit
abbr -a gst git status
**fish's downside — not POSIX**
fish is not POSIX compatible. `[ ]`, `&&`, `||` and other syntax differ. You cannot import `.zshrc` or `.bashrc` straight away. The usual compromise: keep shared scripts in bash, use fish for interactive shells.
**What the 4.0 Rust rewrite delivered**
- Start-up time roughly halved.
- Lower memory usage.
- The C++ memory-safety hazards went away.
- Lower barrier to entry for external contributors.
Chapter 4 · Nushell — structured-data pipelines
Nushell launched in 2019, started by Jonathan Turner and Yehuda Katz. It is written in Rust, and May 2026 stable is 0.105. The pitch is **"every command exchanges data (tables)"**, inspired by PowerShell but fused with Unix philosophy, so it stays lighter and faster.
**Signature — table-driven pipes**
Take a directory listing as a table, filter by size, sort, take 5
ls | where size > 1mb | sort-by modified | first 5
JSON parses automatically
http get https://api.github.com/repos/nushell/nushell | get stargazers_count
CSV → JSON in one line
open data.csv | to json | save data.json
**Why it matters**
In bash land you cobble together `awk`, `cut`, `jq`, `xargs` for text pipelines. Once parsing logic stacks up, readability dies. Nushell treats the table itself as a first-class object, so `where`, `sort-by`, `group-by`, `select`, `each` give you SQL-flavored expressions.
**Limitations**
- It does not run bash scripts as-is.
- External commands speak text, so you still convert with `from json` / `from csv`.
- Being 0.x, syntax shifts release to release — safer for interactive analysis than production scripting.
**Who is it for?**
- Data engineers — a delightful local shell for small data wrangling.
- Cloud operators — `kubectl get pods -o json | from json | where ...` is natural.
Chapter 5 · Murex · xonsh · Elvish — third-path shells
**Murex — a shell with a type system**
A British developer, Andrew Stewart, builds it. May 2026 sits in the 6.x line. Variables carry types and JSON/YAML/TOML are first-class. A compromise between Nushell and bash compatibility.
Declare a variable type
set: int counter = 0
set: string name = "world"
JSON handling is first-class
open my.json -> [name age] -> format json
**xonsh — a shell scripted in Python**
Anthony Scopatz started it in 2015. Mix Python and shell in one file. Beloved by data scientists.
.xonshrc — Python and shell interleaved
echo @(os.getcwd())
Shell output becomes a Python list
ls = $(ls -la).split('\n')
for line in ls:
print(line)
**Elvish — the expressive shell**
Qi Xiao (China) launched it in 2014, written in Go. Lambdas, closures, first-class functions live inside the shell. An early "structured shell" experiment that predated Nushell.
fn greet [name]{
echo "Hello, "$name
}
greet world
**What they have in common**
They trade POSIX compatibility for expressiveness. Using them for init scripts on production servers is risky, but as a personal laptop interactive shell or for data work they're attractive.
Chapter 6 · POSIX shell · dash · ksh · tcsh — legacy and minimalism
**POSIX shell** is the shell IEEE 1003.1 defines. All the shiny features of bash 4+ (`[[ ]]`, `(())`, `declare -A`, `<<<`) are off the table. In exchange, **it runs almost everywhere Unix runs**.
**dash** (Debian Almquist SHell) is the minimal POSIX shell Debian adopted as `/bin/sh`. On Ubuntu and Debian, `/bin/sh` is dash. It starts 4–10x faster than bash, which keeps init scripts snappy.
POSIX-compatible script template
#!/bin/sh
set -eu
No [[ ]], only [ ]
if [ "$1" = "build" ]; then
echo "building..."
fi
No arrays either — use positional parameters
set -- a b c
for x in "$@"; do echo "$x"; done
**ksh** (Korn Shell) was created by David Korn at Bell Labs in 1983. Once the standard on Solaris and AIX. **ksh93** is the last major release; the ksh93u+m fork from 2020 keeps it alive. You'll only run into it on legacy AIX/Solaris systems.
**tcsh** is the C shell's descendant. Some BSDs used it as the default. Common in 1990s university Unix labs. By 2026 it is nearly unused — FreeBSD keeping it as root's default is about it.
**When to use what**
- **Production init/cron scripts** → POSIX `/bin/sh` (dash/ash). Fast and dependable.
- **Developer laptop general scripts** → bash 5.2. Rich features, sea of documentation.
- **Interactive daily shell** → zsh or fish.
- **AIX/Solaris legacy** → ksh.
Chapter 7 · Shell scripting strict mode — set -euo pipefail
Every script that opens with `#!/usr/bin/env bash` should put `set -euo pipefail` on the second line.
#!/usr/bin/env bash
set -euo pipefail
IFS=$'\n\t'
**What each option prevents**
- `set -e` (`errexit`) — exits immediately if a command returns non-zero. **Does not apply inside `if`/`while`/`||`** (deliberate exception).
- `set -u` (`nounset`) — undefined variables become errors, blocking silent typo failures.
- `set -o pipefail` — if any command in a pipeline fails, the whole pipeline fails. Default behavior only inspects the last command's exit code.
- `IFS=$'\n\t'` — restricts word splitting to newline/tab, safer for filenames with spaces.
**The trap with `set -e`**
`set -e` is not absolute. It does nothing inside `cmd | other`, `cmd || true`, `if cmd; then ...`. So you need `pipefail` paired with it. `set -e` also interacts oddly with traps inside functions — **when traps are involved, explicit `if` checks are safer**.
**Comparison — a well-formed script header**
#!/usr/bin/env bash
vim: set ft=bash ts=2 sw=2 :
set -Eeuo pipefail # -E propagates ERR traps into functions
IFS=$'\n\t'
Debug mode: BASH_X=1 ./run.sh
if [[ "${BASH_X:-0}" == 1 ]]; then
set -x
fi
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly TMP_DIR="$(mktemp -d)"
trap 'rm -rf "$TMP_DIR"' EXIT
Chapter 8 · trap and mktemp — never forget cleanup
If a script creates temporary files, **you must clean them up**. A bare `rm` skips error paths. Use `trap`.
TMP=$(mktemp -d -t myapp.XXXXXX)
trap 'rm -rf "$TMP"' EXIT
More elaborate: split by signal
cleanup() {
local exit_code=$?
rm -rf "$TMP"
if [[ $exit_code -ne 0 ]]; then
echo "script terminated abnormally (code=$exit_code)" >&2
fi
exit $exit_code
}
trap cleanup EXIT
trap 'echo "got INT"; exit 130' INT
trap 'echo "got TERM"; exit 143' TERM
**`mktemp` best practices**
- Always create a directory with `-d` and place files inside — cleanup becomes one line.
- Pass `-t prefix.XXXXXX` for easier debugging.
- macOS `mktemp` flags differ from GNU. For cross-platform stick to the short form `mktemp -d "tmp.XXXXXX"`.
**Why not just use `/tmp/myscript`**
Three reasons. 1) **TOCTOU attack** — someone pre-creating the same name to bypass permissions. 2) **Collisions** — two concurrent runs collide. 3) **Cannot clean up safely** — deleting another instance's files is dangerous.
Chapter 9 · Argument parsing — getopts and beyond
**Start with `getopts`**
It's a bash builtin, no external dependencies. Only single-character options, but that covers 99% of cases.
usage() {
cat <<'USAGE'
Usage: deploy.sh [-e env] [-n name] [-d]
-e env deployment environment (dev/staging/prod, required)
-n name service name (default: $(basename $PWD))
-d dry-run mode
-h this help
USAGE
}
DRY_RUN=0
NAME="$(basename "$PWD")"
while getopts ":e:n:dh" opt; do
case $opt in
e) ENV="$OPTARG" ;;
n) NAME="$OPTARG" ;;
d) DRY_RUN=1 ;;
h) usage; exit 0 ;;
\?) echo "unknown option: -$OPTARG" >&2; usage; exit 2 ;;
:) echo "option -$OPTARG needs an argument" >&2; usage; exit 2 ;;
esac
done
shift $((OPTIND - 1))
: "${ENV:?ENV is required (-e)}"
**For long options — roll your own**
Handle long options like --help, --env=dev manually
while [[ $# -gt 0 ]]; do
case $1 in
--env=*) ENV="${1#*=}"; shift ;;
--env) ENV="$2"; shift 2 ;;
--help|-h) usage; exit 0 ;;
--) shift; break ;;
-*) echo "unknown option: $1" >&2; exit 2 ;;
*) ARGS+=("$1"); shift ;;
esac
done
**Alternatives — bashly / docopt.sh**
For complex CLIs, generate code with bashly (chapter 12) or auto-build a parser from your help text with docopt.sh.
Chapter 10 · Arrays and associative arrays — the core of bash 4+
**Indexed arrays**
arr=("a" "b" "c")
echo "${arr[0]}" # a
echo "${arr[@]}" # every element
echo "${#arr[@]}" # element count
arr+=("d") # append
unset 'arr[1]' # drop index 1 (mind sparse arrays after this)
**Associative arrays** — bash 4.0+, standard in zsh, **broken on macOS bash 3.2**
declare -A users
users[alice]=admin
users[bob]=guest
users[carol]=admin
for u in "${!users[@]}"; do
echo "$u: ${users[$u]}"
done
Key existence check
[[ -v users[alice] ]] && echo "alice exists"
**Passing arrays to functions — bash's eternal trap**
Wrong — the array flattens into one argument
print_arr() { for x in "$@"; do echo "$x"; done; }
arr=(a "b c" d)
print_arr "${arr[@]}" # Correct — quoted + @ expansion passes element-by-element
Safer — nameref (bash 4.3+)
print_by_ref() {
local -n ref=$1
for x in "${ref[@]}"; do echo "$x"; done
}
print_by_ref arr
Chapter 11 · Functions, local, readonly, process substitution
**Function guidelines**
Functions always use local. Otherwise globals leak.
greet() {
local name=$1
local greeting=${2:-"Hello"}
echo "$greeting, $name!"
}
Return values go to stdout; exit codes are 0 / non-zero
add() {
local a=$1 b=$2
echo $((a + b))
}
sum=$(add 3 4)
**`readonly` for constants**
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly TIMEOUT_SECONDS=30
readonly -a ENVS=(dev staging prod)
**Process substitution — `<()` and `>()`**
A trick for what pipes cannot express. Avoids temporary files by feeding file descriptors directly.
diff between two commands
diff <(sort file1) <(sort file2)
Tee one output into multiple downstream commands
program | tee >(grep ERROR > err.log) >(grep WARN > warn.log) > full.log
Pass content through an fd to a command that expects a file
psql -f <(echo "SELECT * FROM users WHERE id=$ID")
Chapter 12 · ShellCheck · shfmt · bashly — the modern shell workflow
**ShellCheck — the ESLint for shell**
Vidar Holen (Norway) launched it in 2012. Written in Haskell. May 2026 sits around 0.10. The standard practice: run ShellCheck on every PR.
In CI
shellcheck script.sh
Frequent findings
SC2086: $var is unquoted — word-splitting hazard
SC2046: $(...) result is unquoted
SC2155: declare/local combined with command substitution loses the exit code
SC2034: variable declared but never used
**shfmt — the shell formatter**
Daniel Martí (mvdan) maintains this Go tool. It sits next to shellcheck. `gofmt`-style consistent indentation and ordering for shell scripts.
Indent two spaces, indent case patterns, handle stdin redirection
shfmt -i 2 -ci -sr -d script.sh
pre-commit hook example
shfmt -l -w . # auto-format every .sh file
**bashly — full-stack CLI in bash**
Built by Danny Ben Shitrit. Define the command tree in YAML and bashly emits a complete bash CLI — argument parsing, help, version, completion.
src/bashly.yml
name: mycli
help: my CLI tool
version: 1.0.0
commands:
- name: deploy
help: deploy to an environment
args:
- name: env
required: true
allowed: [dev, staging, prod]
flags:
- long: --dry-run
short: -d
help: dry-run mode
bashly generate
./mycli deploy prod --dry-run
Chapter 13 · fzf · ripgrep · fd · bat · eza — modern Unix tools
**fzf — the fuzzy finder**
Junegunn Choi (Korea, Kakao) started it in 2013. Written in Go. May 2026 sits around 0.55. One of the most influential open-source projects ever shipped by a Korean developer.
fzf basics
ls | fzf
history | fzf
git log --oneline | fzf | awk '{print $1}' | xargs git show
Key bindings (Ctrl+R for history, Ctrl+T for files, Alt+C for directories)
source /usr/share/fzf/key-bindings.bash
**ripgrep (rg) — fast grep**
Andrew Gallant (BurntSushi) built it. Written in Rust. 5–10x faster than grep, and it respects `.gitignore` by default.
Basic usage
rg "TODO" --type rust
rg -i "error" -C 2 # case-insensitive, 2 lines of context
rg --files-with-matches "Apache"
**fd — find replacement**
David Peter (sharkdp) ships this Rust tool. More intuitive than find and faster.
fd "\.rs$" # every Rust file
fd -e py -t f # Python files only
fd -H "node_modules" # include hidden
**bat — cat with syntax**
Also by sharkdp. cat plus syntax highlighting and git diff display.
bat README.md
bat -p src/main.rs # plain mode, syntax only
**eza — successor to exa, ls replacement**
A fork of exa maintained by ogham. Actively maintained since 2024.
eza -l --git --icons
eza --tree --level 2
**Why use all of this?**
GNU coreutils ship a 50-year-old interface. Options aren't intuitive, output lacks color, `.gitignore` is invisible. These tools provide **sensible defaults of the 2010s and beyond** — colorized output, indexing, ignore-file awareness, JSON output.
Chapter 14 · System monitoring — bottom · btop · htop · glances
**htop** — Hisham Muhammad's late-1990s classic. Color + mouse + tree view. Still the most widely installed.
**btop** — Aristocratos's C++ successor. Richer visuals, GPU/disk/network graphs, mouse navigation.
**bottom (btm)** — ClementTsang's Rust implementation. Cross-platform with JSON-style config.
**glances** — Nicolas Hennion's Python project. Has a web API / Prometheus exporter for monitoring integration.
**Comparison**
| Tool | Language | Strength | System overhead |
| --- | --- | --- | --- |
| htop | C | classic, stable | lowest |
| btop | C++ | flashy, GPU | medium |
| bottom | Rust | cross-platform, config rich | medium |
| glances | Python | web API, exporter | a bit higher |
bottom config sample (~/.config/bottom/bottom.toml)
[flags]
hide_avg_cpu = false
mem_as_value = true
network_use_binary_prefix = true
Chapter 15 · jq · yq · dasel · gron — structured data processing
**jq — the JSON standard**
Stephen Dolan started it in 2012. A functional JSON query language. Available everywhere.
curl -s api.github.com/users/torvalds | jq '.public_repos'
kubectl get pods -o json | jq '.items[] | .metadata.name'
cat data.json | jq -r '.users[] | select(.age > 30) | .name'
**yq — jq for YAML**
Mike Farah's Go implementation (mikefarah/yq) is the standard. Inherits jq syntax almost verbatim.
yq '.spec.replicas = 3' deployment.yaml
yq '.services | keys' docker-compose.yml
**dasel — cross-format**
Treats JSON, YAML, TOML, XML, CSV with the same query language.
dasel -f config.yaml '.database.host'
dasel put -f config.json '.version' -v '2.0'
**gron — make JSON greppable**
By Tom Hudson (TomNomNom). Flattens JSON into one-line key=value statements.
gron https://api.github.com/users/torvalds | grep public_repos
json.public_repos = 7;
Each tool **slots naturally into a shell pipeline**. That's the point — the shell's ability to deal with JSON/YAML leapt forward in the 2020s.
Chapter 16 · Parallel execution — xargs · parallel · rush
**`xargs -P`** — the simplest parallelism
8 concurrent gzips
ls *.log | xargs -n 1 -P 8 gzip
null-terminated input (safe for tricky filenames)
find . -name '*.log' -print0 | xargs -0 -n 1 -P 8 gzip
**GNU parallel** — Ole Tange's richer engine
parallel -j 8 gzip ::: *.log
parallel --bar 'convert {} {.}.png' ::: *.jpg
parallel --eta wget {} ::: $(cat urls.txt)
GNU parallel has tricky quoting, but supports ETA, progress bars, retry, and remote SSH-distributed execution.
**rush** — shenwei356's (China) Go implementation, easier quoting
rush 'echo Hello, {}' ::: a b c
rush -j 8 'gzip {}' ::: $(ls *.log)
**When to use what**
- Simple parallelism → `xargs -P`. Fast and everywhere.
- Progress bars / retries needed → `parallel`.
- Quoting headaches → `rush`.
Chapter 17 · gum · charm — pretty TUIs from the shell
Charm.sh, run by Toby Padilla and Christian Rocha, is a collection of Go libraries and tools. They turn shell scripts into pretty interactive UIs.
User input (text, multi-select, confirm)
NAME=$(gum input --placeholder "Your name?")
ENV=$(gum choose dev staging prod)
gum confirm "Really deploy?" && deploy.sh "$ENV"
Spinner
gum spin --spinner dot --title "building..." -- ./build.sh
Colored output
gum style --foreground 212 --bold "success!"
Markdown rendering
gum format -- "# Title" "**bold**"
**Why it matters**
A bash script's interactive UI usually ends at `read`. gum lifts that into the modern era — color, spinner, multi-select, file picker. Great for CI/CD review tools, demos, and setup wizards.
**Charm's other tools**
- **glow** — markdown rendering in the terminal.
- **vhs** — scripted terminal GIF recording.
- **bubble tea** — Go TUI framework. lazygit, k9s belong to the same family.
Chapter 18 · just · task · xc · mise — modern task runners
**just** — Casey Rodarmor's modern Makefile
May 2026 sits at 1.40. Written in Rust. Keeps Makefile's good parts and drops the gotchas.
justfile
default: build test
build:
cargo build --release
test:
cargo test
deploy env="dev":
@echo "Deploying to {{env}}"
./scripts/deploy.sh {{env}}
Call bash directly
notes:
#!/usr/bin/env bash
set -euo pipefail
grep -r "TODO" src/
**Why just beats Makefile**
- No tabs-vs-spaces drama.
- Consistent variable expansion (Make's `$$` vs `$` confusion is gone).
- Clearer dependency syntax (`recipe-name: dep1 dep2`).
- No `.PHONY` ceremony — running commands is the default.
**task (taskfile.dev)** — YAML-based
Taskfile.yml
version: '3'
tasks:
build:
cmds:
- go build ./...
test:
deps: [build]
cmds:
- go test ./...
**xc** — task runner that lives in markdown
By Joe Brockwill. Define tasks as code blocks in a README.md.
build
cargo build --release
test
Requires: build
cargo test
**mise (formerly rtx)** — version manager + task runner
Jordan Pittman's Rust tool. Began as an asdf successor; now also ships task running.
.mise.toml
[tools]
node = "20"
python = "3.12"
[tasks.build]
run = "npm run build"
**Selection criteria**
- Simple command bundles → just. Lightest and most intuitive.
- Complex dependency graphs → task (taskfile).
- Version pinning too → mise.
- Self-documenting → xc.
Chapter 19 · Shell prompts — Starship · Oh My Posh · Powerline
**Starship — universal, fast, Rust-built prompt**
Matan Kushner and Tim Sosa started it in 2019. Supports bash, zsh, fish, PowerShell, Nushell, ion, elvish, tcsh, xonsh, cmd. Configured by a single TOML file.
~/.config/starship.toml
add_newline = false
format = "$directory$git_branch$git_status$character"
[directory]
truncation_length = 3
truncate_to_repo = true
[git_branch]
symbol = " "
[character]
success_symbol = "[➜](bold green)"
error_symbol = "[➜](bold red)"
bash
eval "$(starship init bash)"
zsh
eval "$(starship init zsh)"
fish
starship init fish | source
**Oh My Posh — cross-shell, Windows-friendly**
Jan De Dobbeleer's work. Originally PowerShell-only, now everywhere. Popular with Windows users. 100+ themes.
**Powerline — legacy**
Written in Python. Pioneered the first "pretty prompt" generation. Effectively unused in 2026 — Starship took over almost every slot.
**Why Starship became standard**
1) Shell-independent — one config file gives the same prompt anywhere. 2) Fast — Rust-built with background caching. 3) The right information — git branch, language version, Kubernetes context, AWS profile, all auto-detected.
Chapter 20 · AI shells — sgpt · gptme · Warp · Amazon Q
**shell_gpt (sgpt) — TheR1D**
Python-based. Calls the OpenAI API from the shell.
sgpt "find all .log files modified today"
Output: find . -name "*.log" -mtime -1
sgpt --shell "compress all png in this dir"
(Enter to execute, e to edit)
sgpt --code "fibonacci in rust"
**gptme — Bjorn Holm**
Same concept but supports multi-turn conversations and tool calls (read/write/run files). Local LLMs also plug in.
**Warp Terminal — the AI-first terminal**
Run by Zach Lloyd since 2022. Linux support landed in 2024 and stable in 2025. AI command suggestions, AI explain command, notebooks. Free and Pro plans exist.
Warp's # command
how do I rebase from main?
→ Warp suggests the git rebase command and you Enter to run
**Amazon Q Developer (formerly Fig)**
Amazon acquired Fig in 2024 and merged it into the Amazon Q Developer CLI. Completion got stronger and natural-language command generation arrived.
**Aiken · Codex CLI**
Anthropic Claude Code, GitHub Copilot CLI, OpenAI Codex CLI and similar tools claimed their seat as "agents" that execute shell commands directly. The shell is now an interface for **agents, not only humans**.
**Two models for shell + AI**
1) **Suggest mode** — AI proposes a command; the human confirms (sgpt, Warp).
2) **Agent mode** — AI runs the shell directly, observes results, and chooses the next move (Claude Code, Codex CLI).
Agent mode is risky, so containerized or sandboxed execution is the safer pattern.
Chapter 21 · Terminal emulators — Ghostty · Wezterm · Alacritty · Kitty · iTerm2
**Ghostty — Mitchell Hashimoto's new project**
The HashiCorp co-founder (Vagrant, Packer, Terraform) Mitchell Hashimoto launched Ghostty in 2024, with 1.0 stable in 2025 and 1.2 by May 2026. Written in Zig with GPU acceleration, native macOS plus Linux. The signatures: "the defaults are excellent" and "native macOS integration."
~/.config/ghostty/config
font-family = "JetBrains Mono"
font-size = 14
theme = "Catppuccin Mocha"
window-padding-x = 8
window-padding-y = 8
**Wezterm — Wez Furlong**
Written in Rust. Configured in Lua. Tabs/panes, ligatures, embedded SSH client.
local wezterm = require 'wezterm'
return {
font = wezterm.font 'JetBrains Mono',
font_size = 14,
color_scheme = 'Dracula',
hide_tab_bar_if_only_one_tab = true,
}
**Alacritty — Joe Wilm**
Rust-built. Among the fastest terminals. TOML config. No tabs/panes (use with tmux).
**Kitty — Kovid Goyal**
Python plus GPU. Strong image protocol (can display PNG directly).
**iTerm2 — George Nachman**
The macOS classic. Profiles, hotkeys, split panes, search, broadcast — feature dense. Still a macOS standard in 2026.
**Hyper · Tabby**
Electron-based. Low adoption — heavy with weak GPU acceleration.
**Selection criteria**
- macOS lightweight → Ghostty or iTerm2.
- Cross-platform lightweight → Alacritty (+tmux) or Kitty.
- Rich configuration → Wezterm.
- AI integration → Warp.
Chapter 22 · Korea/Japan ops culture — Coupang · LINE · Cybozu · Mercari
**Korea — Coupang shell ops**
Coupang's infrastructure ops team handles massive traffic and, since the early 2020s, has standardized on **bash + Ansible + Terraform**. Shell best practices (strict mode, ShellCheck CI gate) have been adopted as PR rules internally, as their Korean conference talks (if-kakao, AWS Summit Seoul) describe.
**Korea — Kakao and the origin of fzf**
The fzf you met in chapter 13 is the work of Junegunn Choi, a former Kakao engineer. He first built fzf inside Kakao and later open-sourced it on GitHub, where it became a global standard. It counts among the most influential pieces of open source ever shipped by a Korean developer.
**Japan — LINE shell operations**
LINE runs a huge fleet of microservices out of Tokyo. Posts from the LINE Engineering blog covering internal shell-script conventions in the late 2010s are frequently cited in both Korea and Japan — strict mode, mktemp, trap, and ShellCheck are all mandatory.
**Japan — Cybozu's operational tooling**
Cybozu's Kintone team built ops tools in a bash + Ruby + Go combo for years. Internal tools like `kintone-cli` written in bash are documented on Cybozu Developer Network.
**Japan — Mercari's SRE shell culture**
Mercari, running GCP-based microservices, has publicly described (at SRE NEXT, SREcon) a policy of minimizing shell scripting and replacing it with Go CLIs. Yet they still hold bash as the standard for boot, debugging, and emergency ops.
**Shared patterns**
All three companies share: 1) mandatory `set -euo pipefail`, 2) ShellCheck as a CI gate, 3) rewriting scripts over 1,000 lines in Go/Python, 4) standardizing on `#!/usr/bin/env bash`.
Chapter 23 · Where the shell goes 2026–2028
**Outlook 1 — Bash 5.3 to 6.0**
5.3 arrives within 2026, and 6.0 likely lands around 2027–2028. No big shifts — Bash's philosophy is "don't break compatibility." Internal cleanup, performance, and error messages keep improving.
**Outlook 2 — fish 4's Rust effect**
Since the 2024 Rust rewrite, start-up time and memory have dropped sharply. 2026–2028 should bring more external contributors and faster delivery of new features (structured data, etc.).
**Outlook 3 — Nushell crossing the threshold**
Entering production scripting is still hard, but it is solidifying its place as an interactive shell for data engineers and cloud operators. Once 1.0 ships (predicted 2027) adoption will accelerate.
**Outlook 4 — AI integration standardization**
Agent-mode shell usage will go mainstream. Along with it, **sandboxing**, **permission limits**, and **command logging** will become built-in features of the shell. Agents like Claude Code and Codex CLI treat the shell as a "second user."
**Outlook 5 — Rust rewrites of core tools**
ripgrep, fd, eza, bat, bottom, just, mise, Starship — nearly every modern tool is already Rust. By 2028 we'll likely see the "Unix-core-tool generation change to Rust" mostly complete.
**Outlook 6 — POSIX's place**
POSIX shell isn't going anywhere. It survives as init script, Docker `alpine`, embedded Linux, and the lowest common denominator of CI matrices. So **portable scripts should still ship `#!/bin/sh`** as the best practice.
Chapter 24 · Shell scripting checklist (10 items)
Ten things to verify every time you write a shell script.
1. **shebang** — `#!/usr/bin/env bash` or, when portable, `#!/bin/sh`.
2. **strict mode** — `set -Eeuo pipefail` on the second line.
3. **IFS** — `IFS=$'\n\t'` for safety on filenames with spaces.
4. **mktemp** — always `mktemp -d` for temporaries with `trap '...' EXIT` to clean up.
5. **Quoting** — `$var` is almost always quoted as `"$var"`. `${arr[@]}` becomes `"${arr[@]}"`.
6. **Exit codes** — function results to stdout, success is 0, failure is non-zero.
7. **shellcheck** — required in CI. Suppress warnings explicitly with `# shellcheck disable=...`.
8. **shfmt** — formatting consistency. Run as a pre-commit hook.
9. **Error messages** — send to stderr (`>&2`) and exit with a specific code.
10. **Documentation** — top comment block with usage, arguments, dependencies, examples.
Chapter 25 · A catalog of seven real-world anti-patterns
**Anti-pattern 1 — unquoted variables**
Dangerous
rm $file # explodes if $file has spaces or globs
Safe
rm -- "$file"
**Anti-pattern 2 — relying on `set -e` without `pipefail`**
In `curl ... | jq .x`, if curl fails but jq succeeds on empty input, the pipeline looks successful.
**Anti-pattern 3 — not checking `cd`**
Dangerous
cd /tmp/build
rm -rf * # if cd fails, you blow up the current directory
Safe
cd /tmp/build || exit 1
rm -rf -- ./*
**Anti-pattern 4 — mixing `[ ]` and `[[ ]]`**
Bash uses `[[ ]]` (a keyword). Use `[ ]` only for POSIX compatibility. Do not mix.
**Anti-pattern 5 — backticks (\`cmd\`)**
Use `$(cmd)`. Nestable, quoting-safe, more readable.
**Anti-pattern 6 — `eval`**
Never feed user input to `eval`. There is nearly always another way (arrays, functions, `bash -c "$(...)"`).
**Anti-pattern 7 — a monstrous single script**
Over 1,000 lines? Consider rewriting in Go/Python. The shell's strength is glue — wiring other tools together. Business logic belongs in a real language.
Chapter 26 · Conclusion — the shell still sits in the middle of infrastructure
37 years ago when Brian Fox first released bash, he was building "a free Bourne shell replacement" as part of GNU. That single decision now lives at the innermost layer of nearly every computer.
The 2026 shell landscape summarized:
- **Bash 5.2** is the standard — works everywhere, the scripting default.
- **Zsh 5.9** is macOS default — interactive only.
- **fish 4** is reborn in Rust — friendly interactive.
- **Nushell** is the structured-data shell — for data engineers and cloud operators.
- **ShellCheck + shfmt + bashly** are the heart of the modern workflow.
- **fzf, ripgrep, fd, bat, eza, just, mise, Starship** are the next-generation tools.
- **gum** brings pretty TUIs to the shell.
- **sgpt, Warp, Claude Code** ship AI integration.
- **Ghostty, Wezterm, Alacritty, Kitty** are the next-gen terminals.
The shell isn't going away. It grows richer, safer, and more expressive. Our job is to pick the right tools, write robustly with strict mode and shellcheck, and have the courage to leave the shell when scripts exceed 1,000 lines.
> The shell lives at the center of operations. As long as operations exist, the shell does too.
References
1. GNU Bash Manual — [https://www.gnu.org/software/bash/manual/](https://www.gnu.org/software/bash/manual/)
2. Bash 5.2 Release Notes — [https://lists.gnu.org/archive/html/bug-bash/2022-09/msg00214.html](https://lists.gnu.org/archive/html/bug-bash/2022-09/msg00214.html)
3. Zsh Documentation — [https://zsh.sourceforge.io/Doc/](https://zsh.sourceforge.io/Doc/)
4. Oh My Zsh — [https://ohmyz.sh/](https://ohmyz.sh/)
5. fish shell — [https://fishshell.com/](https://fishshell.com/)
6. Nushell Book — [https://www.nushell.sh/book/](https://www.nushell.sh/book/)
7. Murex — [https://murex.rocks/](https://murex.rocks/)
8. xonsh — [https://xon.sh/](https://xon.sh/)
9. Elvish — [https://elv.sh/](https://elv.sh/)
10. ShellCheck — [https://www.shellcheck.net/](https://www.shellcheck.net/)
11. shfmt (mvdan) — [https://github.com/mvdan/sh](https://github.com/mvdan/sh)
12. bashly — [https://bashly.dannyb.co/](https://bashly.dannyb.co/)
13. gum (Charm) — [https://github.com/charmbracelet/gum](https://github.com/charmbracelet/gum)
14. fzf — [https://github.com/junegunn/fzf](https://github.com/junegunn/fzf)
15. ripgrep — [https://github.com/BurntSushi/ripgrep](https://github.com/BurntSushi/ripgrep)
16. fd — [https://github.com/sharkdp/fd](https://github.com/sharkdp/fd)
17. bat — [https://github.com/sharkdp/bat](https://github.com/sharkdp/bat)
18. eza — [https://github.com/eza-community/eza](https://github.com/eza-community/eza)
19. bottom — [https://github.com/ClementTsang/bottom](https://github.com/ClementTsang/bottom)
20. just — [https://github.com/casey/just](https://github.com/casey/just)
21. taskfile.dev — [https://taskfile.dev/](https://taskfile.dev/)
22. mise — [https://mise.jdx.dev/](https://mise.jdx.dev/)
23. Starship — [https://starship.rs/](https://starship.rs/)
24. Oh My Posh — [https://ohmyposh.dev/](https://ohmyposh.dev/)
25. Warp — [https://www.warp.dev/](https://www.warp.dev/)
26. Ghostty — [https://ghostty.org/](https://ghostty.org/)
27. Wezterm — [https://wezfurlong.org/wezterm/](https://wezfurlong.org/wezterm/)
28. Alacritty — [https://alacritty.org/](https://alacritty.org/)
29. POSIX Shell Standard — [https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html](https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html)
30. Google Shell Style Guide — [https://google.github.io/styleguide/shellguide.html](https://google.github.io/styleguide/shellguide.html)
현재 단락 (1/571)
June 1989. Brian Fox released the first version of the Bourne-Again SHell as part of the GNU Project...