格式
对于你正在修改的文件,应遵循已有的风格,但以下规则对所有新代码都是必须的。
缩进
缩进 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 放在与 if、for 或 while 同一行上。
Shell 中的控制流语句有些不同,但我们遵循与声明函数时大括号相同的原则。
即:; then 和 ; do 应与 if/for/while/until/select 在同一行上。
else 应单独一行,结束语句(fi 和 done)也应单独一行,
并与开始语句垂直对齐。
示例:
# 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}"
doneCase 语句
- 分支选项缩进 2 个空格。
- 单行分支需要在模式的右括号后和
;;前各有一个空格。 - 长的或多命令的分支应拆分为多行,模式、操作和
;;各占一行。
匹配表达式从 case 和 esac 缩进一级。多行操作再缩进一级。通常不需要给匹配
表达式加引号。模式表达式前不应加左括号。避免使用 ;& 和 ;;& 符号。
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 "$#, $@")