Skip to Content
Shell格式

格式

对于你正在修改的文件,应遵循已有的风格,但以下规则对所有新代码都是必须的。

缩进

缩进 2 个空格。不使用制表符(Tab)。

使用空行分隔代码块以提高可读性。缩进为两个空格。无论如何,不要使用制表符。 对于已有文件,保持与现有缩进一致。

例外: 使用制表符的唯一例外情况是 <<- 制表符缩进的此处文档(Here Document) 的正文。

行长度和长字符串

最大行长度为 80 个字符。

如果你必须编写超过 80 个字符的字面字符串,应尽可能使用 此处文档(Here Document)  或嵌入的换行符来实现。

超过 80 个字符且无法合理拆分的单词是可以接受的,但应尽可能将这些内容放在单独的 一行上,或提取到变量中。示例包括文件路径和 URL,特别是在对它们进行字符串匹配 (如 grep)对维护有价值的场景。

# DO use 'here document's cat <<END I am an exceptionally long string. END # Embedded newlines are ok too long_string="I am an exceptionally long string." long_file="/i/am/an/exceptionally/loooooooooooooooooooooooooooooooooooooooooooooooooooong_file" long_string_with_long_file="i am including an exceptionally \ /very/long/file\ in this long string." # Long file converted into a shorter variable name with cleaner line breaking. long_string_alt="i am an including an exceptionally ${long_file} in this long\ string"
# Just because a line contains an exception doesn't mean the rest of the # line shouldn't be wrapped like usual. bad_long_string_with_long_file="i am including an exceptionally /very/long/file in this long string."

管道(Pipeline)

如果管道不能全部放在一行上,应每行一个进行拆分。

如果管道能全部放在一行上,就应该放在一行上。

如果不能,则应在每个管道段处拆分,管道符(Pipe)放在新行上,下一段管道缩进 2 个空格。 应始终使用 \ 来表示续行。这适用于使用 | 组合的命令链, 也适用于使用 ||&& 的逻辑组合。

# All fits on one line command1 | command2 # Long commands command1 \ | command2 \ | command3 \ | command4

这有助于在区分管道和普通的长命令续行时提高可读性,特别是在一行同时使用两者的情况下。

注释需要放在整个管道之前。如果注释和管道都很大且复杂,则值得考虑使用辅助函数 将低层细节移到一边。

控制流

; then; do 放在与 ifforwhile 同一行上。

Shell 中的控制流语句有些不同,但我们遵循与声明函数时大括号相同的原则。 即:; then; do 应与 if/for/while/until/select 在同一行上。 else 应单独一行,结束语句(fidone)也应单独一行, 并与开始语句垂直对齐。

示例:

# If inside a function remember to declare the loop variable as # a local to avoid it leaking into the global environment: local dir for dir in "${dirs_to_cleanup[@]}"; do if [[ -d "${dir}/${SESSION_ID}" ]]; then log_date "Cleaning up old files in ${dir}/${SESSION_ID}" rm "${dir}/${SESSION_ID}/"* || error_message else mkdir -p "${dir}/${SESSION_ID}" || error_message fi done

虽然在 for 循环中可以 省略 in "$@", 但我们建议为了清晰起见始终包含它。

for arg in "$@"; do echo "argument: ${arg}" done

Case 语句

  • 分支选项缩进 2 个空格。
  • 单行分支需要在模式的右括号后和 ;; 前各有一个空格。
  • 长的或多命令的分支应拆分为多行,模式、操作和 ;; 各占一行。

匹配表达式从 caseesac 缩进一级。多行操作再缩进一级。通常不需要给匹配 表达式加引号。模式表达式前不应加左括号。避免使用 ;&;;& 符号。

case "${expression}" in a) variable="…" some_command "${variable}" "${other_expr}" ;; absolute) actions="relative" another_command "${actions}" "${other_expr}" ;; *) error "Unexpected expression '${expression}'" ;; esac

简单命令可以与模式 ;; 放在同一行,只要表达式保持可读性即可。 这在处理单字母选项时通常是合适的。当操作无法放在一行时, 将模式单独放一行,然后是操作,;; 也单独放一行。 当与操作在同一行时,在模式的右括号后和 ;; 前各加一个空格。

verbose='false' aflag='' bflag='' files='' while getopts 'abf:v' flag; do case "${flag}" in a) aflag='true' ;; b) bflag='true' ;; f) files="${OPTARG}" ;; v) verbose='true' ;; *) error "Unexpected option ${flag}" ;; esac done

变量展开(Variable Expansion)

按优先级排序:与已有代码保持一致;给变量加引号;优先使用 "${var}" 而非 "$var"

这些是强烈推荐的指导原则,但不是强制性规定。然而,它是推荐而非强制并不意味着 可以轻视或忽略。

按优先级列出如下:

  • 与已有代码保持一致。
  • 给变量加引号,参见下面的引号部分
  • 除非严格必要或为了避免深度混淆,否则不要对单字符 Shell 特殊变量/位置参数 使用大括号分隔。

其他所有变量优先使用大括号分隔。

# Section of *recommended* cases. # Preferred style for 'special' variables: echo "Positional: $1" "$5" "$3" echo "Specials: !=$!, -=$-, _=$_. ?=$?, #=$# *=$* @=$@ \$=$$ …" # Braces necessary: echo "many parameters: ${10}" # Braces avoiding confusion: # Output is "a0b0c0" set -- a b c echo "${1}0${2}0${3}0" # Preferred style for other variables: echo "PATH=${PATH}, PWD=${PWD}, mine=${some_var}" while read -r f; do echo "file=${f}" done < <(find /tmp)
# Section of *discouraged* cases # Unquoted vars, unbraced vars, brace-delimited single letter # shell specials. echo a=$avar "b=$bvar" "PID=${$}" "${1}" # Confusing use: this is expanded as "${1}0${2}0${3}0", # not "${10}${20}${30} set -- a b c echo "$10$20$30"

注意:在 ${var} 中使用大括号不是一种引号形式。仍然必须同时使用”双引号”。

引号(Quoting)

  • 始终给包含变量、命令替换、空格或 Shell 元字符的字符串加引号, 除非需要谨慎的未加引号展开,或者它是一个 Shell 内部整数(见下一条)。
  • 使用数组(Array)安全地引用元素列表,特别是命令行标志。 参见下面的数组部分。
  • 可选择性地给 Shell 内部的只读 特殊变量  加引号,这些变量被定义为整数:$?$#$$$!。 为了一致性,优先给”命名的”内部整数变量加引号,例如 PPID 等。
  • 优先给”单词”类的字符串加引号(相对于命令选项或路径名)。
  • 注意 [[ … ]] 中模式匹配的引号规则。 参见下面的 Test、[ … ][[ … ]] 部分。
  • 使用 "$@" 除非你有特定原因使用 $*,比如简单地将参数追加到消息或日志 中的字符串。
# 'Single' quotes indicate that no substitution is desired. # "Double" quotes indicate that substitution is required/tolerated. # Simple examples # "quote command substitutions" # Note that quotes nested inside "$()" don't need escaping. flag="$(some_command and its args "$@" 'quoted separately')" # "quote variables" echo "${flag}" # Use arrays with quoted expansion for lists. declare -a FLAGS FLAGS=( --foo --bar='baz' ) readonly FLAGS mybinary "${FLAGS[@]}" # It's ok to not quote internal integer variables. if (( $# > 3 )); then echo "ppid=${PPID}" fi # "never quote literal integers" value=32 # "quote command substitutions", even when you expect integers number="$(generate_number)" # "prefer quoting words", not compulsory readonly USE_INTEGER='true' # "quote shell meta characters" echo 'Hello stranger, and well met. Earn lots of $$$' echo "Process $$: Done making \$\$\$." # "command options or path names" # ($1 is assumed to contain a value here) grep -li Hugo /dev/null "$1" # Less simple examples # "quote variables, unless proven false": ccs might be empty git send-email --to "${reviewers}" ${ccs:+"--cc" "${ccs}"} # Positional parameter precautions: $1 might be unset # Single quotes leave regex as-is. grep -cP '([Ss]pecial|\|?characters*)$' ${1:+"$1"} # For passing on arguments, # "$@" is right almost every time, and # $* is wrong almost every time: # # * $* and $@ will split on spaces, clobbering up arguments # that contain spaces and dropping empty strings; # * "$@" will retain arguments as-is, so no args # provided will result in no args being passed on; # This is in most cases what you want to use for passing # on arguments. # * "$*" expands to one argument, with all args joined # by (usually) spaces, # so no args provided will result in one empty string # being passed on. # # Consult # https://www.gnu.org/software/bash/manual/html_node/Special-Parameters.html and # https://mywiki.wooledge.org/BashGuide/Arrays for more (set -- 1 "2 two" "3 three tres"; echo $#; set -- "$*"; echo "$#, $@") (set -- 1 "2 two" "3 three tres"; echo $#; set -- "$@"; echo "$#, $@")
Last updated on