1.コメント
1.1ヘッダ
- 各ドキュメントの先頭には、ファイルの内容を記述することです。
各ファイルは、トップレベルのコメント、その内容の簡単な概要が含まれている必要があります。著作権および著作者の情報はオプションです。
例えば:
#!/bin/bash # # Perform hot backups of Oracle databases.
1.2機能注釈
- 明らかにし、任意の短い機能は注釈を付けなければなりません両方ではありません。任意のライブラリ関数は関係なく、長さと複雑さの注釈を付けなければなりません。
(もしあれば、ヘルプ情報)のコメントを読むことによって他の人がコードを読み取る必要がなくてもプログラムやライブラリ関数を使用する方法を学ぶことができるようになります。
すべてのコメントは機能が含まれている必要があります。
- 説明機能
- 使用して、グローバル変数を変更
- 使用されるパラメータ
- 戻り値ではなく、コマンドを実行した後、デフォルトの終了ステータス
例えば:
#!/bin/bash # # Perform hot backups of Oracle databases. export PATH='/usr/xpg4/bin:/usr/bin:/opt/csw/bin:/opt/goog/bin' ####################################### # Cleanup files from the backup dir # Globals: # BACKUP_DIR # ORACLE_SID # Arguments: # None # Returns: # None ####################################### cleanup() { ... }
1.3。実装は、セクションのコメント
- あなたのコードは、ヒント、明白な、面白いか、重要な部分が含まれているコメント。
Googleのコードのコメントを追跡するのが一般的のこの部分。すべてのコードをコメントしないでください。あなたは複雑なアルゴリズムを持っている、またはあなたが別の何かをやっている場合は、簡単なコメントを置きます。
1.4.TODOコメント
- 使用TODOは、一時的、短期的な解決策、または十分ではありますが、完璧ではないコードをコメントしています。
これは、C ++ガイドの規則と一致しています。
TODOの括弧内は、ユーザー名に続いて、すべて大文字の文字列のTODOを、含まれている必要があります。コロンはオプションです。エントリー後のバグまたは好ましくは一緒にチケット番号をTODOと。
例えば:
# TODO(mrmonkey): Handle the unlikely edge cases (bug ####)
2.フォーマット
2.1インデント
- インデント二つのスペース、タブなし。
読みやすさを向上させるためのコードブロックの間に空白行を使用してください。インデント二つの空間。何をするにしても、タブを使用しないでください。既存のファイルの場合、既存のインデントを保ちます。
2.2。行の長さと文字列の長さ
- 最大ライン長さは80個の文字です。
あなたは長さの文字列に80個の以上の文字を書くことがある場合は、可能であれば、ここでは文書や改行を使用するようにしてください。長い文字列の80文字を超えると、適切に分割することができない、これは正常です。強く、それを短くする方法を見つけることをお勧めします。
# 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."
2.3。パイプライン
- 行は、行ごとにパイプセクションに全体のパイプライン動作、全体のパイプのセット動作に耐えることができない場合。
全体のパイプライン動作のためのライン十分に大きい場合は、同じライン上で動作パイプライン全体を囲みます。
そうでなければ、各ラインの全体のパイプライン動作は、パイプが改行文字やインデント二つの空間に管の一部で動作しなければならない、パイプ部分に分割されるべきです。「|」これは、パイプを使用するために適用され、コマンドの組み合わせチェーン論理演算の「||」と「&&」チェーンを使用しました。
# All fits on one line command1 | command2 # Long commands command1 \ | command2 \ | command3 \ | command4
2.4。サイクル
- してください 、 そして 、 、 同じ行に。
; do
; then
while
for
if
シェルサイクルはわずかに異なっているが、我々は同じ括弧をたどるとき、原則宣言を持つ関数。つまり、 、 必要があり、同じライン上にある間に/ /のための場合。 これは、単一の行である必要があり、文の終わりには、単一の行で始まる必要があり、文に対して垂直に配向されます。; do
; then
else
例えば:
for dir in ${dirs_to_cleanup}; do if [[ -d "${dir}/${ORACLE_SID}" ]]; then log_date "Cleaning up old files in ${dir}/${ORACLE_SID}" rm "${dir}/${ORACLE_SID}/"* if [[ "$?" -ne 0 ]]; then error_message fi else mkdir -p "${dir}/${ORACLE_SID}" if [[ "$?" -ne 0 ]]; then error_message fi fi done
2.5.case声明
- 二つの空間利用可能なオプションでインデント。
- 同じラインモードのオプション次の右括弧終末と
;;
それぞれの前にスペースが必要です。 - 任意の長さまたは複数のコマンドオプションは、複数の行、パターン、およびターミネーターオペレータに分割しなければならない
;;
異なる行です。
表現比と一致する 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
2.6。変数の展開
- 優先度の高い順に:;あなたは変数を参照し、何を見つけると一致推奨
${var}
代わりに$var
、以下に詳細に説明します。
必須要件は非常に論争のようだとするので、これらは、唯一のガイドラインです。
優先度の高い順に以下に挙げます。
- あなたが発見した既存のコードと一致しています。
- 参照変数の参照の下に参照してください。
- 深い混乱を避けるために絶対に必要かない限り、シェル変数または引用符で囲まれた個々の文字の変数の位置に特別な括弧を使用しないでください。中括弧を持つ他のすべての変数ことをお勧めします。
# 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 f; do echo "file=${f}" done < <(ls -l /tmp) # Section of discouraged cases # Unquoted vars, unbraced vars, brace-quoted 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"
2.7。参考文献
- あなたは慎重に参照することなく展開する必要がある場合を除き、それ以外の場合は常に変数は文字、スペース、またはシェルのメタ文字を置き換えるために、参照、コマンド文字列が含まれています。
- 推奨引用は、(むしろ、コマンドオプションまたはパス名より)単語列です。
- 整数を引用しないでください。
- なお、
[[
基準パターンマッチングルール。 - 使用してください
$@
あなたが使用する特定の理由がない限り$*
。
# 'Single' quotes indicate that no substitution is desired. # "Double" quotes indicate that substitution is required/tolerated. # Simple examples # "quote command substitutions" flag="$(some_command and its args "$@" 'quoted separately')" # "quote variables" echo "${flag}" # "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 everytime, and # $* is wrong almost everytime: # # * $* 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 'man bash' for the nit-grits ;-) set -- 1 "2 two" "3 three tres"; echo $# ; set -- "$*"; echo "$#, $@") set -- 1 "2 two" "3 three tres"; echo $# ; set -- "$@"; echo "$#, $@")
3.特性及错误
3.1.命令替换
- 使用
$(command)
而不是反引号。
嵌套的反引号要求用反斜杠转义内部的反引号。而 $(command)
形式嵌套时不需要改变,而且更易于阅读。
例如:
# This is preferred: var="$(command "$(command1)")" # This is not: var="`command \`command1\``"
3.2.test,[和[[
- 推荐使用
[[ ... ]]
,而不是[
,test
, 和/usr/bin/ [
。
因为在 [[
和 ]]
之间不会有路径名称扩展或单词分割发生,所以使用 [[ ... ]]
能够减少错误。而且 [[ ... ]]
允许正则表达式匹配,而 [ ... ]
不允许。
# This ensures the string on the left is made up of characters in the # alnum character class followed by the string name. # Note that the RHS should not be quoted here. # For the gory details, see # E14 at http://tiswww.case.edu/php/chet/bash/FAQ if [[ "filename" =~ ^[[:alnum:]]+name ]]; then echo "Match" fi # This matches the exact pattern "f*" (Does not match in this case) if [[ "filename" == "f*" ]]; then echo "Match" fi # This gives a "too many arguments" error as f* is expanded to the # contents of the current directory if [ "filename" == f* ]; then echo "Match" fi
3.3.测试字符串
尽可能使用引用,而不是过滤字符串。
Bash足以在测试中处理空字符串。所以,请使用空(非空)字符串测试,而不是过滤字符,使得代码更易于阅读。
# Do this: if [[ "${my_var}" = "some_string" ]]; then do_something fi # -z (string length is zero) and -n (string length is not zero) are # preferred over testing for an empty string if [[ -z "${my_var}" ]]; then do_something fi # This is OK (ensure quotes on the empty side), but not preferred: if [[ "${my_var}" = "" ]]; then do_something fi # Not this: if [[ "${my_var}X" = "some_stringX" ]]; then do_something fi
为了避免对你测试的目的产生困惑,请明确使用`-z`或者`-n`
# Use this if [[ -n "${my_var}" ]]; then do_something fi # Instead of this as errors can occur if ${my_var} expands to a test # flag if [[ "${my_var}" ]]; then do_something fi
3.4.文件名的通配符扩展
- 当进行文件名的通配符扩展时,请使用明确的路径。
因为文件名可能以 -
开头,所以使用扩展通配符 ./*
比 *
来得安全得多。
# Here's the contents of the directory: # -f -r somedir somefile # This deletes almost everything in the directory by force psa@bilby$ rm -v * removed directory: `somedir' removed `somefile' # As opposed to: psa@bilby$ rm -v ./* removed `./-f' removed `./-r' rm: cannot remove `./somedir': Is a directory removed `./somefile'
3.5.Eval
- 应该避免使用eval。
当用于给变量赋值时,Eval解析输入,并且能够设置变量,但无法检查这些变量是什么。
# What does this set? # Did it succeed? In part or whole? eval $(set_my_variables) # What happens if one of the returned values has a space in it? variable="$(eval some_function)"
3.6.管道导向while循环
- 请使用过程替换或者for循环,而不是管道导向while循环。在while循环中被修改的变量是不能传递给父shell的,因为循环命令是在一个子shell中运行的。
管道导向while循环中的隐式子shell使得追踪bug变得很困难。
last_line='NULL' your_command | while read line; do last_line="${line}" done # This will output 'NULL' echo "${last_line}"
如果你确定输入中不包含空格或者特殊符号(通常意味着不是用户输入的),那么可以使用一个for循环。
total=0 # Only do this if there are no spaces in return values. for value in $(command); do total+="${value}" done
使用过程替换允许重定向输出,但是请将命令放入一个显式的子shell中,而不是bash为while循环创建的隐式子shell。
total=0 last_file= while read count filename; do total+="${count}" last_file="${filename}" done < <(your_command | uniq -c) # This will output the second field of the last line of output from # the command. echo "Total = ${total}" echo "Last one = ${last_file}"
当不需要传递复杂的结果给父shell时可以使用while循环。这通常需要一些更复杂的“解析”。请注意简单的例子使用如awk这类工具可能更容易完成。当你特别不希望改变父shell的范围变量时这可能也是有用的。
# Trivial implementation of awk expression: # awk '$3 == "nfs" { print $2 " maps to " $1 }' /proc/mounts cat /proc/mounts | while read src dest type opts rest; do if [[ ${type} == "nfs" ]]; then echo "NFS ${dest} maps to ${src}" fi done