bash-shell
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseBash Shell Style Guide Skill
Bash Shell风格指南技能
Apply Google's Shell Style Guide conventions to write clean, maintainable, and secure Bash scripts.
遵循Google的Shell风格指南规范,编写整洁、可维护且安全的Bash脚本。
Overview
概述
This skill provides comprehensive guidance for writing professional Bash shell scripts following Google's established conventions. Use this when creating new shell scripts, reviewing existing code, or establishing shell scripting standards for a project.
本技能为遵循Google既定规范编写专业Bash shell脚本提供全面指导。适用于创建新shell脚本、审查现有代码或为项目制定shell脚本标准时使用。
When to Use Shell Scripts
何时使用Shell脚本
Shell scripts are appropriate for:
- Small utilities and simple wrapper scripts
- Tasks primarily calling other utilities with minimal data manipulation
- Scripts under 100 lines with straightforward control flow
Avoid shell scripts when:
- Performance is critical
- Complex data manipulation is required
- The script exceeds 100 lines or uses non-straightforward control flow
- Maintainability by others is a concern
If writing a script that grows beyond these limits, rewrite in a more structured language early to avoid costly rewrites later.
Shell脚本适用于:
- 小型工具和简单的包装器脚本
- 主要调用其他工具、仅需少量数据处理的任务
- 代码量在100行以内、控制流简单的脚本
应避免使用Shell脚本的场景:
- 对性能要求较高时
- 需要复杂数据处理时
- 脚本代码量超过100行或控制流复杂时
- 需要保障其他人员可维护性时
如果编写的脚本超出上述限制,应尽早改用更结构化的语言重写,避免后续付出高昂的重写成本。
Essential Requirements
核心要求
Shebang and Shell Selection
Shebang与Shell选择
Always use Bash for executable shell scripts:
bash
#!/bin/bashKey points:
- Bash is the only permitted shell scripting language for executables
- Use to configure shell options for consistent behavior
set - No need to strive for POSIX compatibility unless required by legacy systems
可执行shell脚本请始终使用Bash:
bash
#!/bin/bash关键点:
- Bash是唯一允许用于可执行脚本的shell脚本语言
- 使用配置shell选项以保证行为一致性
set - 除非遗留系统要求,无需刻意追求POSIX兼容性
File Extensions
文件扩展名
Executables:
- Use extension if a build rule will rename the source file
.sh - Use no extension if the executable goes directly into user's
PATH
Libraries:
- Must have extension
.sh - Should not be executable
可执行文件:
- 如果构建规则会重命名源文件,使用扩展名
.sh - 如果可执行文件直接放入用户的中,无需添加扩展名
PATH
库文件:
- 必须使用扩展名
.sh - 不应设置为可执行
Core Style Guidelines
核心风格指南
Indentation and Formatting
缩进与格式
Indentation: 2 spaces, no tabs
bash
if [[ -f "${config_file}" ]]; then
source "${config_file}"
fiLine length: Maximum 80 characters
For long strings, use here-documents or embedded newlines:
bash
undefined缩进:2个空格,禁止使用制表符
bash
if [[ -f "${config_file}" ]]; then
source "${config_file}"
fi行长度:最大80个字符
对于长字符串,使用here-document或嵌入换行符:
bash
undefinedUsing here-document
使用here-document
cat <<END
This is a long message that
spans multiple lines.
END
cat <<END
This is a long message that
spans multiple lines.
END
Using embedded newlines
使用嵌入换行符
long_string="This is a long message
that spans multiple lines."
undefinedlong_string="This is a long message
that spans multiple lines."
undefinedControl Flow
控制流
Put and on the same line as control statements:
; then; dobash
undefined将和与控制语句放在同一行:
; then; dobash
undefinedCorrect
正确写法
for dir in "${dirs_to_cleanup[@]}"; do
if [[ -d "${dir}" ]]; then
rm -rf "${dir}"
fi
done
for dir in "${dirs_to_cleanup[@]}"; do
if [[ -d "${dir}" ]]; then
rm -rf "${dir}"
fi
done
Loop variables should be local in functions
循环变量应在函数中声明为局部变量
local dir
for dir in "${dirs_to_cleanup[@]}"; do
Process directory
done
undefinedlocal dir
for dir in "${dirs_to_cleanup[@]}"; do
处理目录
done
undefinedVariable Expansion and Quoting
变量展开与引用
Variable expansion: Prefer over
"${var}""$var"bash
undefined变量展开:优先使用而非
"${var}""$var"bash
undefinedPreferred
推荐写法
echo "PATH=${PATH}, PWD=${PWD}, mine=${some_var}"
echo "PATH=${PATH}, PWD=${PWD}, mine=${some_var}"
Acceptable for special variables
特殊变量可使用简化写法
echo "Positional: $1" "$5" "$3"
echo "Exit status: $?"
**Quoting rules**:
- Always quote strings containing variables, command substitutions, spaces, or shell meta characters
- Use arrays for safe quoting of lists
- Use `"$@"` for passing arguments (not `$*`)
```bashecho "Positional: $1" "$5" "$3"
echo "Exit status: $?"
**引用规则**:
- 包含变量、命令替换、空格或shell元字符的字符串必须加引号
- 使用数组安全引用列表
- 使用`"$@"`传递参数(而非`$*`)
```bashQuote variables
引用变量
echo "${flag}"
echo "${flag}"
Quote command substitutions
引用命令替换
flag="$(some_command and its args "$@")"
flag="$(some_command and its args "$@")"
Use arrays for lists
使用数组存储列表
declare -a FLAGS
FLAGS=(--foo --bar='baz')
mybinary "${FLAGS[@]}"
undefineddeclare -a FLAGS
FLAGS=(--foo --bar='baz')
mybinary "${FLAGS[@]}"
undefinedTesting and Conditionals
测试与条件判断
Use over :
[[ … ]][ … ]bash
undefined优先使用而非:
[[ … ]][ … ]bash
undefinedPreferred - supports pattern matching
推荐写法 - 支持模式匹配
if [[ "${filename}" =~ ^[[:alnum:]]+name ]]; then
echo "Match"
fi
if [[ "${filename}" =~ ^[[:alnum:]]+name ]]; then
echo "Match"
fi
String comparisons
字符串比较
if [[ "${my_var}" == "some_string" ]]; then
do_something
fi
if [[ "${my_var}" == "some_string" ]]; then
do_something
fi
Test for empty strings
测试空字符串
if [[ -z "${my_var}" ]]; then
echo "Variable is empty"
fi
if [[ -z "${my_var}" ]]; then
echo "Variable is empty"
fi
Test for non-empty strings
测试非空字符串
if [[ -n "${my_var}" ]]; then
echo "Variable is not empty"
fi
**Use `(( … ))` for arithmetic**:
```bashif [[ -n "${my_var}" ]]; then
echo "Variable is not empty"
fi
**使用`(( … ))`进行算术运算**:
```bashArithmetic comparisons
算术比较
if (( my_var > 3 )); then
do_something
fi
if (( my_var > 3 )); then
do_something
fi
Calculations
计算
local -i hundred="$(( 10 * 10 ))"
(( i += 3 ))
undefinedlocal -i hundred="$(( 10 * 10 ))"
(( i += 3 ))
undefinedFunctions
函数
Function syntax:
bash
undefined函数语法:
bash
undefinedSingle function
单个函数
my_func() {
local arg1="$1"
local result
result="$(process "${arg1}")"
echo "${result}"
}
my_func() {
local arg1="$1"
local result
result="$(process "${arg1}")"
echo "${result}"
}
Package-namespaced function
带命名空间的函数
mypackage::my_func() {
…
}
**Function comments**: Required for non-obvious functions
```bash
#######################################mypackage::my_func() {
…
}
**函数注释**:非直观函数必须添加注释
```bash
#######################################Cleanup files from the backup directory.
清理备份目录中的文件。
Globals:
全局变量:
BACKUP_DIR
BACKUP_DIR
ORACLE_SID
ORACLE_SID
Arguments:
参数:
None
无
Returns:
返回值:
0 on success, non-zero on error
成功返回0,失败返回非0值
#######################################
cleanup() {
rm -rf "${BACKUP_DIR}/${ORACLE_SID}/"*
}
undefined#######################################
cleanup() {
rm -rf "${BACKUP_DIR}/${ORACLE_SID}/"*
}
undefinedNaming Conventions
命名规范
Functions and variables: lowercase with underscores
bash
my_function() {
local my_variable="value"
}Constants and environment variables: UPPERCASE with underscores
bash
readonly PATH_TO_FILES='/some/path'
declare -xr ORACLE_SID='PROD'Loop variables: Descriptive names matching what you're looping through
bash
for zone in "${zones[@]}"; do
process_zone "${zone}"
done函数与变量:小写字母加下划线
bash
my_function() {
local my_variable="value"
}常量与环境变量:大写字母加下划线
bash
readonly PATH_TO_FILES='/some/path'
declare -xr ORACLE_SID='PROD'循环变量:使用与循环对象匹配的描述性名称
bash
for zone in "${zones[@]}"; do
process_zone "${zone}"
doneError Handling
错误处理
Output to STDERR
输出到STDERR
Error messages go to STDERR:
bash
err() {
echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $*" >&2
}
if ! do_something; then
err "Unable to do_something"
exit 1
fi错误信息输出到STDERR:
bash
err() {
echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $*" >&2
}
if ! do_something; then
err "Unable to do_something"
exit 1
fiCheck Return Values
检查返回值
Always check return values:
bash
undefined必须检查返回值:
bash
undefinedDirect check
直接检查
if ! mv "${file}" "${dest_dir}/"; then
echo "Unable to move ${file} to ${dest_dir}" >&2
exit 1
fi
if ! mv "${file}" "${dest_dir}/"; then
echo "Unable to move ${file} to ${dest_dir}" >&2
exit 1
fi
Using $?
使用$?
mv "${file}" "${dest_dir}/"
if (( $? != 0 )); then
echo "Unable to move ${file}" >&2
exit 1
fi
mv "${file}" "${dest_dir}/"
if (( $? != 0 )); then
echo "Unable to move ${file}" >&2
exit 1
fi
Check pipeline status
检查管道状态
tar -cf - ./* | (cd "${dir}" && tar -xf -)
if (( PIPESTATUS[0] != 0 || PIPESTATUS[1] != 0 )); then
echo "Unable to tar files to ${dir}" >&2
fi
undefinedtar -cf - ./* | (cd "${dir}" && tar -xf -)
if (( PIPESTATUS[0] != 0 || PIPESTATUS[1] != 0 )); then
echo "Unable to tar files to ${dir}" >&2
fi
undefinedBest Practices
最佳实践
Use ShellCheck
使用ShellCheck
Run ShellCheck on all scripts to identify common bugs and issues.
对所有脚本运行ShellCheck以识别常见bug与问题。
Command Substitution
命令替换
Use instead of backticks:
$(command)bash
undefined使用而非反引号:
$(command)bash
undefinedPreferred
推荐写法
var="$(command "$(command1)")"
var="$(command "$(command1)")"
Avoid
避免写法
var="command1``"
command \undefinedvar="command1``"
command \undefinedArrays
数组
Use arrays for lists to avoid quoting issues:
bash
undefined使用数组存储列表以避免引用问题:
bash
undefinedGood
正确写法
declare -a files
files=(file1.txt file2.txt "file with spaces.txt")
for file in "${files[@]}"; do
process "${file}"
done
declare -a files
files=(file1.txt file2.txt "file with spaces.txt")
for file in "${files[@]}"; do
process "${file}"
done
Avoid
避免写法
files="file1.txt file2.txt file with spaces.txt"
for file in ${files}; do # Breaks on spaces
process "${file}"
done
undefinedfiles="file1.txt file2.txt file with spaces.txt"
for file in ${files}; do # 遇到空格会出错
process "${file}"
done
undefinedLocal Variables
局部变量
Declare function-specific variables with :
localbash
my_func() {
local name="$1"
local my_var
# Separate declaration and assignment for command substitution
my_var="$(get_value)"
(( $? == 0 )) || return
}使用声明函数专属变量:
localbash
my_func() {
local name="$1"
local my_var
# 命令替换时分开声明与赋值
my_var="$(get_value)"
(( $? == 0 )) || return
}Main Function
主函数
For scripts with multiple functions, use a function:
mainbash
main() {
local config_file="$1"
if [[ ! -f "${config_file}" ]]; then
err "Config file not found: ${config_file}"
return 1
fi
process_config "${config_file}"
}
main "$@"对于包含多个函数的脚本,使用函数:
mainbash
main() {
local config_file="$1"
if [[ ! -f "${config_file}" ]]; then
err "Config file not found: ${config_file}"
return 1
fi
process_config "${config_file}"
}
main "$@"Avoid Common Pitfalls
避免常见陷阱
Don't use - It's unsafe and makes debugging difficult
evalWildcard expansion - Use explicit paths:
bash
undefined不要使用 - 不安全且难以调试
eval通配符展开 - 使用明确路径:
bash
undefinedGood
正确写法
rm -v ./*
rm -v ./*
Bad - files starting with - cause issues
错误写法 - 以-开头的文件会引发问题
rm -v *
**Pipes to while** - Use process substitution or `readarray`:
```bashrm -v *
**管道到while循环** - 使用进程替换或`readarray`:
```bashGood - preserves variables
正确写法 - 保留变量
while read -r line; do
last_line="${line}"
done < <(your_command)
while read -r line; do
last_line="${line}"
done < <(your_command)
Or use readarray
或使用readarray
readarray -t lines < <(your_command)
for line in "${lines[@]}"; do
process "${line}"
done
readarray -t lines < <(your_command)
for line in "${lines[@]}"; do
process "${line}"
done
Bad - creates subshell, variables don't persist
错误写法 - 创建子shell,变量无法在外部访问
your_command | while read -r line; do
last_line="${line}" # Won't be visible outside
done
undefinedyour_command | while read -r line; do
last_line="${line}" # 外部无法访问该变量
done
undefinedQuick Reference
快速参考
File header:
bash
#!/bin/bash
#文件头:
bash
#!/bin/bash
#Brief description of what this script does.
Brief description of what this script does.
**Error output function**:
```bash
err() {
echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $*" >&2
}Standard patterns:
bash
undefined
**错误输出函数**:
```bash
err() {
echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $*" >&2
}标准模式:
bash
undefinedCheck command success
检查命令执行成功
if ! command; then
err "Command failed"
exit 1
fi
if ! command; then
err "Command failed"
exit 1
fi
Test string equality
测试字符串相等
if [[ "${var}" == "value" ]]; then
do_something
fi
if [[ "${var}" == "value" ]]; then
do_something
fi
Test numeric comparison
测试数值比较
if (( num > 10 )); then
do_something
fi
if (( num > 10 )); then
do_something
fi
Loop over array
遍历数组
for item in "${array[@]}"; do
process "${item}"
done
for item in "${array[@]}"; do
process "${item}"
done
Safe command substitution
安全的命令替换
result="$(command)"
if (( $? != 0 )); then
err "Command failed"
exit 1
fi
undefinedresult="$(command)"
if (( $? != 0 )); then
err "Command failed"
exit 1
fi
undefinedAdditional Resources
额外资源
Reference Files
参考文档
For comprehensive style rules and patterns:
- - Complete Google Shell Style Guide reference
references/google-shell-guide.md - - Frequently used patterns and idioms
references/common-patterns.md - - Security best practices for shell scripts
references/security-guidelines.md
如需全面的风格规则与模式:
- - 完整的Google Shell风格指南参考
references/google-shell-guide.md - - 常用模式与惯用写法
references/common-patterns.md - - Shell脚本安全最佳实践
references/security-guidelines.md
Example Scripts
示例脚本
Working examples in :
examples/- - Simple utility script template
basic-script.sh - - Script with functions, error handling, and argument parsing
advanced-script.sh - - Reusable function library example
library-example.sh
examples/- - 简单工具脚本模板
basic-script.sh - - 包含函数、错误处理与参数解析的脚本
advanced-script.sh - - 可复用函数库示例
library-example.sh
When in Doubt
存疑时的原则
Be consistent:
- Follow existing style in the codebase
- Consistency allows automation and reduces cognitive load
- Pick one style and stick with it throughout the project
保持一致性:
- 遵循代码库中的现有风格
- 一致性支持自动化并降低认知负担
- 选择一种风格并在整个项目中坚持使用