📦 deps(thirdparty): update snapshots
This commit is contained in:
@@ -0,0 +1,292 @@
|
||||
---
|
||||
name: bash
|
||||
description: "Language-specific super-code guidelines for bash."
|
||||
risk: safe
|
||||
source: community
|
||||
date_added: "2026-06-16"
|
||||
---
|
||||
# Bash / Shell: Idiomatic Efficiency Reference
|
||||
|
||||
## Table of Contents
|
||||
1. [Quoting & Word Splitting](#quoting)
|
||||
2. [Conditionals & Tests](#conditionals)
|
||||
3. [Loops & Iteration](#loops)
|
||||
4. [Pipes & Process Substitution](#pipes)
|
||||
5. [Functions & Return Values](#functions)
|
||||
6. [Error Handling](#errors)
|
||||
7. [Anti-patterns specific to Bash](#antipatterns)
|
||||
|
||||
---
|
||||
|
||||
## 1. Quoting & Word Splitting {#quoting}
|
||||
|
||||
```bash
|
||||
# ❌ Unquoted variable (word splitting + globbing)
|
||||
for f in $files; do rm $f; done
|
||||
|
||||
# ✅
|
||||
for f in "${files[@]}"; do rm -- "$f"; done
|
||||
```
|
||||
|
||||
```bash
|
||||
# ❌ Unquoted command substitution
|
||||
path=$(find . -name config)
|
||||
cat $path # breaks on spaces
|
||||
|
||||
# ✅
|
||||
path="$(find . -name config)"
|
||||
cat "$path"
|
||||
```
|
||||
|
||||
```bash
|
||||
# ❌ Using backticks for command substitution
|
||||
result=`echo hello`
|
||||
|
||||
# ✅ — $() nests cleanly
|
||||
result=$(echo hello)
|
||||
```
|
||||
|
||||
```bash
|
||||
# ❌ String comparison without quotes
|
||||
if [ $var = "hello" ]; then # breaks if var is empty or has spaces
|
||||
|
||||
# ✅
|
||||
if [[ "$var" = "hello" ]]; then
|
||||
```
|
||||
|
||||
**Rule: double-quote every `$variable` and `$(command)` unless you specifically need splitting.**
|
||||
|
||||
---
|
||||
|
||||
## 2. Conditionals & Tests {#conditionals}
|
||||
|
||||
```bash
|
||||
# ❌ Single bracket test (POSIX but fragile)
|
||||
if [ -f "$file" -a -r "$file" ]; then
|
||||
|
||||
# ✅ — [[ is safer, supports &&/||, no word splitting inside
|
||||
if [[ -f "$file" && -r "$file" ]]; then
|
||||
```
|
||||
|
||||
```bash
|
||||
# ❌ Testing command exit status with if [ $? -eq 0 ]
|
||||
grep -q pattern file
|
||||
if [ $? -eq 0 ]; then echo "found"; fi
|
||||
|
||||
# ✅ — test command directly
|
||||
if grep -q pattern file; then echo "found"; fi
|
||||
```
|
||||
|
||||
```bash
|
||||
# ❌ String equality with == outside [[
|
||||
if [ "$a" == "$b" ]; then # == not POSIX in [ ]
|
||||
|
||||
# ✅
|
||||
if [[ "$a" == "$b" ]]; then # Bash
|
||||
# or POSIX:
|
||||
if [ "$a" = "$b" ]; then
|
||||
```
|
||||
|
||||
```bash
|
||||
# ❌ Arithmetic with [ ] and string comparison
|
||||
if [ "$count" -gt 10 ]; then
|
||||
|
||||
# ✅ — (( )) for arithmetic
|
||||
if (( count > 10 )); then
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Loops & Iteration {#loops}
|
||||
|
||||
```bash
|
||||
# ❌ Parsing ls output
|
||||
for f in $(ls *.txt); do process "$f"; done
|
||||
|
||||
# ✅ — glob directly
|
||||
for f in *.txt; do
|
||||
[[ -e "$f" ]] || continue # handle no-match
|
||||
process "$f"
|
||||
done
|
||||
```
|
||||
|
||||
```bash
|
||||
# ❌ Reading file line-by-line with for
|
||||
for line in $(cat file.txt); do # splits on words, not lines
|
||||
|
||||
# ✅
|
||||
while IFS= read -r line; do
|
||||
process "$line"
|
||||
done < file.txt
|
||||
```
|
||||
|
||||
```bash
|
||||
# ❌ Seq for counting
|
||||
for i in $(seq 1 10); do
|
||||
|
||||
# ✅ — brace expansion (Bash)
|
||||
for i in {1..10}; do
|
||||
# or C-style:
|
||||
for (( i = 1; i <= 10; i++ )); do
|
||||
```
|
||||
|
||||
```bash
|
||||
# ❌ Processing command output line-by-line with pipe (subshell trap)
|
||||
count=0
|
||||
cat file.txt | while read -r line; do
|
||||
(( count++ )) # count resets after loop — subshell
|
||||
done
|
||||
echo "$count" # always 0
|
||||
|
||||
# ✅ — redirect, not pipe
|
||||
count=0
|
||||
while IFS= read -r line; do
|
||||
(( count++ ))
|
||||
done < file.txt
|
||||
echo "$count"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Pipes & Process Substitution {#pipes}
|
||||
|
||||
```bash
|
||||
# ❌ Chained grep | grep for AND
|
||||
grep "error" log.txt | grep "timeout"
|
||||
|
||||
# ✅ — single grep with pattern
|
||||
grep -E "error.*timeout|timeout.*error" log.txt
|
||||
# or awk for complex logic:
|
||||
awk '/error/ && /timeout/' log.txt
|
||||
```
|
||||
|
||||
```bash
|
||||
# ❌ cat + pipe (UUOC — Useless Use of Cat)
|
||||
cat file.txt | grep pattern
|
||||
|
||||
# ✅
|
||||
grep pattern file.txt
|
||||
```
|
||||
|
||||
```bash
|
||||
# ❌ Temp file for diff between commands
|
||||
cmd1 > /tmp/a.txt
|
||||
cmd2 > /tmp/b.txt
|
||||
diff /tmp/a.txt /tmp/b.txt
|
||||
rm /tmp/a.txt /tmp/b.txt
|
||||
|
||||
# ✅ — process substitution
|
||||
diff <(cmd1) <(cmd2)
|
||||
```
|
||||
|
||||
```bash
|
||||
# ❌ Ignoring pipe failures (only last command's exit code)
|
||||
false | true
|
||||
echo $? # 0 — false's failure hidden
|
||||
|
||||
# ✅
|
||||
set -o pipefail
|
||||
false | true
|
||||
echo $? # 1
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Functions & Return Values {#functions}
|
||||
|
||||
```bash
|
||||
# ❌ Using return for string values
|
||||
get_name() {
|
||||
return "Alice" # return is for exit codes (0-255)
|
||||
}
|
||||
|
||||
# ✅ — echo + capture
|
||||
get_name() {
|
||||
echo "Alice"
|
||||
}
|
||||
name=$(get_name)
|
||||
```
|
||||
|
||||
```bash
|
||||
# ❌ Global variables modified inside functions
|
||||
result=""
|
||||
compute() { result="done"; }
|
||||
|
||||
# ✅ — use local, return via stdout
|
||||
compute() {
|
||||
local tmp
|
||||
tmp=$(do_work)
|
||||
echo "$tmp"
|
||||
}
|
||||
result=$(compute)
|
||||
```
|
||||
|
||||
```bash
|
||||
# ❌ Function keyword (not POSIX)
|
||||
function my_func {
|
||||
|
||||
# ✅
|
||||
my_func() {
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Error Handling {#errors}
|
||||
|
||||
```bash
|
||||
# ❌ No error handling — script continues after failure
|
||||
cd /some/dir
|
||||
rm -rf * # if cd fails, deletes from wrong directory
|
||||
|
||||
# ✅
|
||||
set -euo pipefail
|
||||
|
||||
cd /some/dir || { echo "cd failed" >&2; exit 1; }
|
||||
rm -rf ./*
|
||||
```
|
||||
|
||||
```bash
|
||||
# ❌ No cleanup on exit
|
||||
tmpfile=$(mktemp)
|
||||
# ... script might exit early, leaving tmpfile
|
||||
|
||||
# ✅ — trap for cleanup
|
||||
tmpfile=$(mktemp)
|
||||
trap 'rm -f "$tmpfile"' EXIT
|
||||
```
|
||||
|
||||
```bash
|
||||
# ❌ Silencing errors blindly
|
||||
command 2>/dev/null
|
||||
|
||||
# ✅ — redirect only when you know what you're suppressing
|
||||
command 2>/dev/null || true # explicit: we expect and accept failure
|
||||
```
|
||||
|
||||
**Start every script with `set -euo pipefail`. Remove selectively where needed.**
|
||||
|
||||
---
|
||||
|
||||
## 7. Anti-patterns specific to Bash {#antipatterns}
|
||||
|
||||
| Anti-pattern | Preferred |
|
||||
|---|---|
|
||||
| Parsing `ls` output | glob: `for f in *.txt` |
|
||||
| `cat file \| grep` | `grep pattern file` |
|
||||
| Unquoted `$var` | `"$var"` always |
|
||||
| `[ ]` for complex tests | `[[ ]]` |
|
||||
| Backtick substitution | `$(command)` |
|
||||
| `$?` check after command | `if command; then` |
|
||||
| `echo` for debug | `printf '%s\n'` (portable) |
|
||||
| No `set -euo pipefail` | always set at script top |
|
||||
| Temp files without cleanup | `trap 'rm -f "$tmp"' EXIT` |
|
||||
| `eval` with user input | avoid; use arrays for dynamic commands |
|
||||
| `#!/bin/sh` with Bash features | `#!/usr/bin/env bash` |
|
||||
| String math `expr 1 + 1` | `$(( 1 + 1 ))` |
|
||||
| `test -z` for number comparison | `(( ))` for arithmetic |
|
||||
|
||||
|
||||
|
||||
## Limitations
|
||||
- These are language-specific guidelines and do not cover overall architectural decisions.
|
||||
- Over-compression might reduce readability; apply judgement.
|
||||
Reference in New Issue
Block a user