Shell Coding Style

1. Comment

1.1 header

  • The beginning of each document is to describe the contents of the file.

Each file must contain a top-level comment, a brief overview of its contents. Copyright notice and author information is optional.

E.g:

#!/bin/bash
#
# Perform hot backups of Oracle databases.

 

1.2 Functional annotation

  • Both obvious and not any short function must be annotated. Any library functions regardless of the length and complexity must be annotated.

Others by reading the comments (and help information, if any) will be able to learn how to use your program or library functions without the need to read the code.

All comments should contain functions:

  • Description function
  • Use and modify global variables
  • Parameters used
  • The return value, rather than the default exit status after running a command

E.g:

#!/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. Implementation comments section

  • Comment your code contains tips, obvious, interesting or important part.

This part of common practice to follow Google's code comments. Do not comment all the code. If you have a complex algorithm, or you're doing something different, put a simple comment.

 

1.4.TODO comments

  • Use TODO comments temporary, short-term solution, or good enough but not perfect code.

This is consistent with C ++ Guide conventions.

TODOs should contain all uppercase string TODO, followed by your user name in parentheses. Colon is optional. Bug or preferably together with the ticket number TODO after entry.

E.g:

# TODO(mrmonkey): Handle the unlikely edge cases (bug ####)

 

2. Format

2.1 indentation

  • Indent two spaces, no tabs.

Use blank lines between the code blocks to improve readability. Indent two spaces. Whatever you do, do not use tabs. For existing files, keep the existing indentation.

2.2. Line length and string length

  • The maximum line length is 80 characters.

If you have to write more than 80 characters in length string, if possible, try to use here document or embedded newline. Longer than 80 characters of the character string and can not be properly divided, this is normal. It is strongly recommended to find a way to make it shorter.

# 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. Pipeline

  • If a line can not tolerate the entire pipeline operation, the set operation of the entire pipe into a pipe section per line.

If a line big enough for the entire pipeline operations, then enclose the entire pipeline operating on the same line.

Otherwise, the entire pipeline operation of each line should be split into a pipe section, the pipe should be operating at a part of the pipe on the new line character and indented two spaces. This applies to use the pipe '|' and using the combined chain of command '||' and '&&' chain of logic operations.

# All fits on one line
command1 | command2

# Long commands
command1 \
  | command2 \
  | command3 \
  | command4

  

2.4. Cycle

  • Please   ,   and   ,   ,   on the same line.dothenwhileforif

the shell cycle is slightly different, but we follow the same braces when the function with the Declaration of Principles. That is, the   ,   should, and if / for / while on the same line.  It should be a single line, the end of the sentence should start with a single line and is aligned perpendicular to the statement.dothenelse

E.g:

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 statement

  • Indented by two spaces available options.
  • Following the same line mode options right parenthesis endings and  ;; before each require a space.
  • Optional length or a multiple command options should be split into multiple lines, patterns, and a terminator operator  ;; in different rows.

Match expression ratio  case and  esac indent level. Multi-line operation again indent level. Under normal circumstances, no reference matching expression. Mode preceding expression should not be left parenthesis appears. Avoid using  ;& and  ;;& symbols.

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

  

As long as the entire expression readable, simple commands can now model and  ;; on the same line. This usually applies to handle single-letter options. When tolerate single line operation, set the separate discharge line mode, then the operation, and end character  ;; is also a separate line. When the same row in the right bracket operation, mode-end and  ;; prior to use separated by a space.

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. Variable expansion

  • In order of priority: consistent with what you find; you reference variables; recommended  ${var} instead  $var , explained in detail below.

These are only guidelines, because as a mandatory requirement seems highly controversial.

Listed below in order of priority.

  1. Consistent with the existing code you have found.
  2. See below a reference variable references.
  3. Unless absolutely necessary or to avoid deep confusion, do not use special braces to shell variables or variable positioning of individual characters in quotes. Recommended that all other variables with curly braces.
# 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. References

  • Unless you need to carefully expand without reference, otherwise always variable contains a reference, the command string to replace characters, spaces, or shell meta characters.
  • Recommended Citation is a word string (rather than command options or path name).
  • Do not quote integers.
  • Note that  [[ the reference pattern matching rules.
  • Please use  $@ unless you have a specific reason to use  $* .
# '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

  

Guess you like

Origin www.cnblogs.com/shwang/p/11822367.html