bash-shell

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Bash 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/bash
Key points:
  • Bash is the only permitted shell scripting language for executables
  • Use
    set
    to configure shell options for consistent behavior
  • No need to strive for POSIX compatibility unless required by legacy systems
可执行shell脚本请始终使用Bash:
bash
#!/bin/bash
关键点
  • Bash是唯一允许用于可执行脚本的shell脚本语言
  • 使用
    set
    配置shell选项以保证行为一致性
  • 除非遗留系统要求,无需刻意追求POSIX兼容性

File Extensions

文件扩展名

Executables:
  • Use
    .sh
    extension if a build rule will rename the source file
  • Use no extension if the executable goes directly into user's
    PATH
Libraries:
  • Must have
    .sh
    extension
  • 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}"
fi
Line 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
undefined

Using 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."
undefined
long_string="This is a long message that spans multiple lines."
undefined

Control Flow

控制流

Put
; then
and
; do
on the same line as control statements:
bash
undefined
; then
; do
与控制语句放在同一行:
bash
undefined

Correct

正确写法

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
undefined
local dir for dir in "${dirs_to_cleanup[@]}"; do

处理目录

done
undefined

Variable Expansion and Quoting

变量展开与引用

Variable expansion: Prefer
"${var}"
over
"$var"
bash
undefined
变量展开:优先使用
"${var}"
而非
"$var"
bash
undefined

Preferred

推荐写法

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 `$*`)

```bash
echo "Positional: $1" "$5" "$3" echo "Exit status: $?"

**引用规则**:
- 包含变量、命令替换、空格或shell元字符的字符串必须加引号
- 使用数组安全引用列表
- 使用`"$@"`传递参数(而非`$*`)

```bash

Quote 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[@]}"
undefined
declare -a FLAGS FLAGS=(--foo --bar='baz') mybinary "${FLAGS[@]}"
undefined

Testing and Conditionals

测试与条件判断

Use
[[ … ]]
over
[ … ]
:
bash
undefined
优先使用
[[ … ]]
而非
[ … ]
bash
undefined

Preferred - 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**:

```bash
if [[ -n "${my_var}" ]]; then echo "Variable is not empty" fi

**使用`(( … ))`进行算术运算**:

```bash

Arithmetic 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 ))
undefined
local -i hundred="$(( 10 * 10 ))" (( i += 3 ))
undefined

Functions

函数

Function syntax:
bash
undefined
函数语法
bash
undefined

Single 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}/"* }
undefined

Naming 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}"
done

Error 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
fi

Check Return Values

检查返回值

Always check return values:
bash
undefined
必须检查返回值:
bash
undefined

Direct 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
undefined
tar -cf - ./* | (cd "${dir}" && tar -xf -) if (( PIPESTATUS[0] != 0 || PIPESTATUS[1] != 0 )); then echo "Unable to tar files to ${dir}" >&2 fi
undefined

Best Practices

最佳实践

Use ShellCheck

使用ShellCheck

Run ShellCheck on all scripts to identify common bugs and issues.
对所有脚本运行ShellCheck以识别常见bug与问题。

Command Substitution

命令替换

Use
$(command)
instead of backticks:
bash
undefined
使用
$(command)
而非反引号:
bash
undefined

Preferred

推荐写法

var="$(command "$(command1)")"
var="$(command "$(command1)")"

Avoid

避免写法

var="
command \
command1``"
undefined
var="
command \
command1``"
undefined

Arrays

数组

Use arrays for lists to avoid quoting issues:
bash
undefined
使用数组存储列表以避免引用问题:
bash
undefined

Good

正确写法

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
undefined
files="file1.txt file2.txt file with spaces.txt" for file in ${files}; do # 遇到空格会出错 process "${file}" done
undefined

Local Variables

局部变量

Declare function-specific variables with
local
:
bash
my_func() {
  local name="$1"
  local my_var
  
  # Separate declaration and assignment for command substitution
  my_var="$(get_value)"
  (( $? == 0 )) || return
}
使用
local
声明函数专属变量:
bash
my_func() {
  local name="$1"
  local my_var
  
  # 命令替换时分开声明与赋值
  my_var="$(get_value)"
  (( $? == 0 )) || return
}

Main Function

主函数

For scripts with multiple functions, use a
main
function:
bash
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 "$@"
对于包含多个函数的脚本,使用
main
函数:
bash
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
eval
- It's unsafe and makes debugging difficult
Wildcard expansion - Use explicit paths:
bash
undefined
不要使用
eval
- 不安全且难以调试
通配符展开 - 使用明确路径:
bash
undefined

Good

正确写法

rm -v ./*
rm -v ./*

Bad - files starting with - cause issues

错误写法 - 以-开头的文件会引发问题

rm -v *

**Pipes to while** - Use process substitution or `readarray`:

```bash
rm -v *

**管道到while循环** - 使用进程替换或`readarray`:

```bash

Good - 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
undefined
your_command | while read -r line; do last_line="${line}" # 外部无法访问该变量 done
undefined

Quick 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
undefined

Check 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
undefined
result="$(command)" if (( $? != 0 )); then err "Command failed" exit 1 fi
undefined

Additional Resources

额外资源

Reference Files

参考文档

For comprehensive style rules and patterns:
  • references/google-shell-guide.md
    - Complete Google Shell Style Guide reference
  • references/common-patterns.md
    - Frequently used patterns and idioms
  • references/security-guidelines.md
    - Security best practices for shell scripts
如需全面的风格规则与模式:
  • references/google-shell-guide.md
    - 完整的Google Shell风格指南参考
  • references/common-patterns.md
    - 常用模式与惯用写法
  • references/security-guidelines.md
    - Shell脚本安全最佳实践

Example Scripts

示例脚本

Working examples in
examples/
:
  • basic-script.sh
    - Simple utility script template
  • advanced-script.sh
    - Script with functions, error handling, and argument parsing
  • library-example.sh
    - Reusable function library example
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
保持一致性:
  • 遵循代码库中的现有风格
  • 一致性支持自动化并降低认知负担
  • 选择一种风格并在整个项目中坚持使用