From bash-hygiene
Bash anti-pattern detection for Claude Code agents -- AP1 overly complex commands, AP2 Unicode in bash strings, AP3 stateful cd, shell quoting hygiene, irreversible operation boundaries.
How this skill is triggered — by the user, by Claude, or both
Slash command
/bash-hygiene:bash-anti-patternsThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
本 Skill 提供系統化的 bash 指令編寫規範,防止 parser 失敗打斷工作流程,
本 Skill 提供系統化的 bash 指令編寫規範,防止 parser 失敗打斷工作流程, 同時設定不可逆操作的 autonomy 邊界。三個 rule 檔可獨立啟用。
踩雷時會看到下列 parser 錯誤,改 escape 也沒用:
Newline followed by # inside a quoted argument(類別 B)Unhandled node type: string(類別 D)以下 5 項,同時出現兩個以上就是複雜度過高:
\ 續行)python -c、node -e、perl -e、jq 多行複雜表達式)${var//pattern/replace}、${!indirect}、${var%suffix} 串接)以下都是合法用法,不構成反模式:
# 合法的 git workflow chain — && 數量不是問題
git add . && git commit -m "feat: add feature" && git push origin feature
# 合法的工具串接
make lint && make test
# 合法的簡單條件(各自獨立,不要合併成 && ... || ...)
[ -f ".env" ] || echo "[WARN] .env not found"
[ -f ".env" ] && source .env
# 避免:[ -f ".env" ] && source .env || echo "..."
# 原因:source 失敗時 || 也會觸發 echo,語意錯誤
「&& 數量本身不是判斷項」 — 問題在每段操作的內部複雜度,不在串接數量。
1. 拆成多個 bash call(最常見也最簡單)
每個 call 解一個問題,agent 看完再決定下一步:
# 錯:一行塞太多
RESULT=$(python3 -c "
import json, sys
data = json.loads(sys.stdin.read())
print(data.get('key', {}).get('nested', ''))
" <<< "$INPUT")
# 對:分兩步
echo "$INPUT" > /tmp/input.json
RESULT=$(jq -r '.key.nested // empty' /tmp/input.json)
2. 寫成獨立 script 檔
放 ~/.claude/scripts/ 或專案 scripts/,從 bash 呼叫 bash <path>:
# 錯:heredoc 內嵌複雜 Python
bash -c "$(cat <<'PYEOF'
import re, sys
for line in sys.stdin:
if re.match(r'pattern', line):
...
PYEOF
)"
# 對:寫成獨立 script,從 bash 呼叫
cat > /tmp/process.py << 'EOF'
import re, sys
for line in sys.stdin:
if re.match(r'pattern', line):
print(line.rstrip())
EOF
python3 /tmp/process.py < input.txt
3. 用對的工具取代 inline 邏輯
| 需求 | 不要用 | 改用 |
|---|---|---|
| JSON 處理 | python3 -c "import json..." | jq |
| 路徑操作 | ${VAR%/*} 字串展開 | dirname / basename / realpath |
| 簡單條件 | if/elif/else 三層 | [ ] + &&/|| 或 case |
| 文字過濾 | inline awk '{if...}' | grep -E 或拆成多個 grep + cut pipe |
範例 A:jq 巢狀條件 → 兩段 pipe
# 錯:jq 多行複雜表達式(內嵌語言 + 多層條件 = score 2)
RESULT=$(jq -r '
if .status == "active" then
.users[] | select(.role == "admin") | .name
else
"inactive"
end
' config.json)
# 對:拆成兩段
STATUS=$(jq -r '.status' config.json)
if [ "$STATUS" = "active" ]; then
RESULT=$(jq -r '.users[] | select(.role == "admin") | .name' config.json)
fi
範例 B:複雜 if/elif → case statement
# 錯:多層 if/elif(多層分支 = score 1,加上複雜參數展開 ${EXT##*.} = score 2)
EXT="${FILENAME##*.}"
if [ "$EXT" = "py" ]; then
RUNNER="python3"
elif [ "$EXT" = "js" ]; then
RUNNER="node"
elif [ "$EXT" = "rb" ]; then
RUNNER="ruby"
elif [ "$EXT" = "sh" ]; then
RUNNER="bash"
else
RUNNER="unknown"
fi
# 對:先用 basename 取副檔名,再用 case
EXT=$(basename "$FILENAME" | cut -d. -f2)
case "$EXT" in
py) RUNNER="python3" ;;
js) RUNNER="node" ;;
rb) RUNNER="ruby" ;;
sh) RUNNER="bash" ;;
*) RUNNER="unknown" ;;
esac
範例 C:heredoc inline Python → 拆出獨立 script
# 錯:heredoc + 內嵌 Python(score 1 + score 3 = 2,過度)
python3 - <<'EOF'
import sys, json
data = json.load(sys.stdin)
for item in data.get('items', []):
if item.get('active'):
print(item['name'])
EOF
# 對:把 Python 寫成獨立檔,cat heredoc 寫檔(score 1 = 可接受)
# 說明:cat > file 的 heredoc 只寫檔,不執行 Python,score 從 2 降到 1
cat > /tmp/filter_active.py << 'EOF'
import sys, json
data = json.load(sys.stdin)
for item in data.get('items', []):
if item.get('active'):
print(item['name'])
EOF
python3 /tmp/filter_active.py < data.json
本規範只限制:bash 指令本身的字元內容
echo 的字串參數不限制:
cat README.md 內含 emoji 沒問題).md 的文字屬檔案層面)以下字元出現在 bash 指令字串內,會卡住 Claude Code bash tool parser:
| 類型 | 範例字元 | Unicode 範圍 |
|---|---|---|
| Em dash | — | U+2014 |
| En dash | – | U+2013 |
| Emoji(圖示類) | 大部分 Unicode 表情符號 | U+1F300–U+1FAFF、U+2600–U+27BF |
| 零寬空白 | (不可見) | U+200B 等 |
CJK 字元、全形標點、ASCII 標點均 OK。
| 原本 | 改成 |
|---|---|
| 跳過圖示(skip 類) | [SKIP] 或 (skipped) |
| 勾選圖示(ok 類) | [OK] 或 (ok) |
| 警告圖示(warn 類) | [WARN] 或 (warn) |
| 失敗圖示(fail 類) | [FAIL] 或 (fail) |
| 火箭圖示(go 類) | [GO] |
| Em dash — | --(ASCII 雙連字號) |
| En dash – | -(ASCII 連字號) |
# 錯:emoji 在 bash echo 字串內(這行會卡 parser)
echo " ⏭ 無 docker-compose,跳過"
# 對:改用 ASCII 替代
echo " [SKIP] 無 docker-compose,跳過"
# 錯:em dash 在 bash echo 字串內
echo "PREREQ: NOT_FOUND — stop here"
# 對:改用 ASCII 雙連字號
echo "PREREQ: NOT_FOUND -- stop here"
# OK:emoji 在 markdown 文件段落(這不是 bash 指令)
# README.md: > ✅ 安裝完成
# OK:bash cat 讀含 emoji 的檔案(emoji 在檔案內,不在 bash 字串)
cat README.md
下 bash 指令前快速自問:
"''" 這種,或 $(cmd "$VAR") 同型衝突)python -c、node -e、jq 多行)cd <path> && 嗎?→ 判斷子類,改用 --directory / git -C / 絕對路徑grep "...\|..." 雙引號 BRE 嗎?→ 改單引號$(outer "$(inner)") 反向巢狀嗎?→ 拆兩 callAP1 門檻:換行 / 引號 / 內嵌語言 三項中任兩項 yes → 拆 bash call / 寫 script / 換工具
# 先註冊 marketplace(一次性)
claude plugin marketplace add howie/ainization-skill
# 安裝 plugin
claude plugin install bash-hygiene@ainization-skill
安裝後自動獲得:
若不用 plugin,可手動將規則存到專案的 .claude/rules/:
13-bash-anti-patterns.md:AP1/AP2/AP3 判斷標準與速查14-shell-quoting-hygiene.md:五類引號錯誤 Rules 1-515-irreversible-operations.md:五類不可逆操作邊界詳細內容見 plugin repo 的 rules-context.md。
Claude Code 的 bash tool 使用簡化 shell parser 而非完整的 bash AST parser:
# 字元、引號巢狀超過一定深度,觸發 parser edge case不需深究實作細節——記住判斷標準與對策就夠。
Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.
npx claudepluginhub heyu-ai/yibi-stack --plugin bash-hygiene