shell-engineering
8
总安装量
8
周安装量
#34446
全站排名
安装命令
npx skills add https://github.com/gonzaloserrano/dotfiles --skill shell-engineering
Agent 安装分布
claude-code
6
gemini-cli
5
antigravity
5
windsurf
5
codex
5
opencode
5
Skill 文档
Shell Engineering
Comprehensive guidelines for writing production-quality shell scripts based on Google’s Shell Style Guide.
When to Use Shell
- Small utilities and simple wrapper scripts
- Scripts calling other tools with straightforward logic
- Rewrite in a structured language (Go, Python) when exceeding ~100 lines or using complex control flow
Shell Choice
- Bash is the only permitted shell for executables
- Start scripts with
#!/bin/bashwith minimal flags - Libraries must have
.shextension and not be executable - SUID/SGID are forbidden on shell scripts
File Structure
#!/bin/bash
#
# Brief description of the script's purpose.
set -euo pipefail
# Constants and environment variables (UPPERCASE)
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly LOG_FILE="/tmp/script.log"
# Source libraries
source "${SCRIPT_DIR}/lib/utils.sh"
# Function definitions (lowercase_with_underscores)
my_function() {
local arg1="$1"
# ...
}
# Main function
main() {
# Script logic here
}
main "$@"
Formatting Rules
Indentation and Length
- 2 spaces for indentation (no tabs)
- 80 characters maximum line length
- Split long pipelines with pipe at line start:
command1 \
| command2 \
| command3
Control Structures
; thenand; doon same line asif/while/for:
if [[ -n "${var}" ]]; then
# ...
fi
for file in "${files[@]}"; do
# ...
done
Quoting
- Always quote strings with variables, command substitutions, or spaces
- Use
"${var}"format with braces for clarity - Use
"$@"not$*for argument lists - Use arrays for lists with spaces in elements
Naming Conventions
| Type | Convention | Example |
|---|---|---|
| Functions | lowercase_underscores |
process_file() |
| Variables | lowercase_underscores |
file_count |
| Constants | UPPERCASE_UNDERSCORES |
readonly MAX_RETRIES=3 |
| Environment vars | UPPERCASE |
export PATH |
| Source files | lowercase_underscores.sh |
string_utils.sh |
Preferred Syntax
Use These
# Command substitution
result=$(command)
# Test conditions
if [[ -n "${var}" ]]; then
# Arithmetic
if (( count > 10 )); then
total=$(( a + b ))
# Local variables in functions
my_func() {
local name="$1"
}
# Arrays for lists
files=("file1.txt" "file2.txt" "file with spaces.txt")
for f in "${files[@]}"; do
Avoid These
# Backticks (use $() instead)
result=`command`
# Single brackets (use [[ ]] instead)
if [ -n "$var" ]; then
# let, expr, $[ ] (use $(( )) instead)
let count=count+1
# eval (security risk)
eval "$cmd"
# Piping to while (loses variable scope)
cat file | while read line; do
# alias in scripts (use functions)
alias ll='ls -la'
# Unquoted wildcards
for f in *; do # Use ./* instead
Error Handling
STDERR for Errors
err() {
echo "[$(date +'%Y-%m-%d %H:%M:%S')] ERROR: $*" >&2
}
if ! process_file "${file}"; then
err "Failed to process ${file}"
exit 1
fi
Check Return Values
# Direct if check
if ! mv "${file}" "${dest}"; then
err "Failed to move file"
fi
# Pipeline status
tar -cf - . | gzip > archive.tar.gz
if (( PIPESTATUS[0] != 0 || PIPESTATUS[1] != 0 )); then
err "Archive creation failed"
fi
Comments and Documentation
File Header (Required)
#!/bin/bash
#
# Script description explaining purpose and usage.
#
# Usage: script.sh [options] <input_file>
Function Documentation
#######################################
# Process a data file and output results.
# Globals:
# OUTPUT_DIR
# Arguments:
# $1 - Input file path
# $2 - Output format (csv|json)
# Outputs:
# Writes processed data to OUTPUT_DIR
# Returns:
# 0 on success, non-zero on error
#######################################
process_data() {
local input_file="$1"
local format="${2:-csv}"
# ...
}
TODO Comments
# TODO(username): Handle edge case for empty input
Testing and Validation
- Use ShellCheck to identify bugs
- Test string emptiness explicitly:
# Good
if [[ -z "${var}" ]]; then # empty
if [[ -n "${var}" ]]; then # non-empty
# Avoid
if [[ "${var}" ]]; then
Built-in Preference
Prefer bash builtins over external commands:
# Good: parameter expansion
filename="${path##*/}"
extension="${filename##*.}"
basename="${filename%.*}"
# Avoid: external commands
filename=$(basename "$path")
extension=$(echo "$filename" | sed 's/.*\.//')
Quick Reference
| Do | Don’t |
|---|---|
$(command) |
`command` |
[[ condition ]] |
[ condition ] |
(( arithmetic )) |
let, expr |
"${var}" |
$var |
"$@" |
$* |
local var |
global variables in functions |
./* wildcards |
* wildcards |
| functions | aliases |
| arrays | space-separated strings |