From af7f6aa63f11bffae77805eb34e7e4c9919691c2 Mon Sep 17 00:00:00 2001 From: Isaac Good Date: Tue, 11 Feb 2020 11:09:33 -0800 Subject: [PATCH] Project import generated by Copybara. PiperOrigin-RevId: 294477904 --- shellguide.md | 145 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 144 insertions(+), 1 deletion(-) diff --git a/shellguide.md b/shellguide.md index 250e3b3..3639fa9 100644 --- a/shellguide.md +++ b/shellguide.md @@ -15,11 +15,68 @@ Revision 2.00 Authored, revised and maintained by many Googlers. -[TOC] +
+ Table of Contents + +- [1 Background](#s1-background) + * [1.1 Which Shell to Use](#s1.1-which-shell-to-use) +- [2 Shell Files and Interpreter Invocation](#s2-shell-files-and-interpreter-invocation) + * [2.1 File Extensions](#s2.1-file-extensions) + * [2.2 SUID/SGID](#s2.2-suid-sgid) +- [3 Environment](#s3-environment) + * [3.1 STDOUT vs STDERR](#s3.1-stdout-vs-stderr) +- [4 Comments](#s4-comments) + * [4.1 File Header](#s4.1-file-header) + + [4.1.1 $Id$](#s4.1.1-id) + + [4.1.2 $Id$ Pros](#s4.1.2-id-pros) + + [4.1.3 $Id$ Cons](#s4.1.3-id-cons) + + [4.1.4 $Id$ Decision](#s4.1.4-id-decision) + * [4.2 Function Comments](#s4.2-function-comments) + * [4.3 Implementation Comments](#s4.3-implementation-comments) + * [4.4 TODO Comments](#s4.4-todo-comments) +- [5 Formatting](#s5-formatting) + * [5.1 Indentation](#s5.1-indentation) + * [5.2 Line Length and Long Strings](#s5.2-line-length-and-long-strings) + * [5.3 Pipelines](#s5.3-pipelines) + * [5.4 Loops](#s5.4-loops) + * [5.5 Case statement](#s5.5-case-statement) + * [5.6 Variable expansion](#s5.6-variable-expansion) + * [5.7 Quoting](#s5.7-quoting) +- [6 Features and Bugs](#s6-features-and-bugs) + * [6.1 ShellCheck](#s6.1-shellcheck) + * [6.2 Command Substitution](#s6.2-command-substitution) + * [6.3 Test, `[ … ]`, and `[[ … ]]`](#s6.3-tests) + * [6.4 Testing Strings](#s6.4-testing-strings) + * [6.5 Wildcard Expansion of Filenames](#s6.5-wildcard-expansion-of-filenames) + * [6.6 Eval](#s6.6-eval) + * [6.7 Arrays](#s6.7-arrays) + + [6.7.1 Arrays Pros](#s6.7.1-arrays-pros) + + [6.7.2 Arrays Cons](#s6.7.2-arrays-cons) + + [6.7.3 Arrays Decision](#s6.7.3-arrays-decision) + * [6.8 Pipes to While](#s6.8-pipes-to-while) + * [6.9 Arithmetic](#s6.9-arithmetic) +- [7 Naming Conventions](#s7-naming-conventions) + * [7.1 Function Names](#s7.1-function-names) + * [7.2 Variable Names](#s7.2-variable-names) + * [7.3 Constants and Environment Variable Names](#s7.3-constants-and-environment-variable-names) + * [7.4 Source Filenames](#s7.4-source-filenames) + * [7.5 Read-only Variables](#s7.5-read-only-variables) + * [7.6 Use Local Variables](#s7.6-use-local-variables) + * [7.7 Function Location](#s7.7-function-location) + * [7.8 main](#s7.8-main) +- [8 Calling Commands](#s8-calling-commands) + * [8.1 Checking Return Values](#s8.1-checking-return-values) + * [8.2 Builtin Commands vs. External Commands](#s8.2-builtin-commands-vs-external-commands) +- [9 Conclusion](#s9-conclusion) + +
+ + ## Background + ### Which Shell to Use @@ -38,6 +95,8 @@ The only exception to this is where you're forced to by whatever you're coding for. One example of this is Solaris SVR4 packages which require plain Bourne shell for any scripts. + + ### When to use Shell Shell should only be used for small utilities or simple wrapper @@ -62,8 +121,12 @@ Some guidelines: to switch languages) consider whether the code is easily maintainable by people other than its author. + + ## Shell Files and Interpreter Invocation + + ### File Extensions Executables should have no extension (strongly preferred) or a @@ -80,6 +143,8 @@ languages. This allows library files with identical purposes but different languages to be identically named except for the language-specific suffix. + + ### SUID/SGID SUID and SGID are *forbidden* on shell scripts. @@ -91,9 +156,12 @@ which is why we're being explicit about banning it. Use `sudo` to provide elevated access if you need it. + ## Environment + + ### STDOUT vs STDERR All error messages should go to `STDERR`. @@ -114,9 +182,12 @@ if ! do_something; then fi ``` + ## Comments + + ### File Header Start each file with a description of its contents. @@ -135,6 +206,8 @@ Example: ``` + + ### Function Comments Any function that is not both obvious and short must be commented. Any @@ -194,6 +267,8 @@ function del_thing(arg) { } ``` + + ### Implementation Comments Comment tricky, non-obvious, interesting or important parts of your @@ -203,6 +278,8 @@ This follows general Google coding comment practice. Don't comment everything. If there's a complex algorithm or you're doing something out of the ordinary, put a short comment in. + + ### TODO Comments Use TODO comments for code that is temporary, a short-term solution, or @@ -227,12 +304,15 @@ Examples: # TODO(mrmonkey): Handle the unlikely edge cases (bug ####) ``` + ## Formatting While you should follow the style that's already there for files that you're modifying, the following are required for any new code. + + ### Indentation Indent 2 spaces. No tabs. @@ -241,6 +321,8 @@ Use blank lines between blocks to improve readability. Indentation is two spaces. Whatever you do, don't use tabs. For existing files, stay faithful to the existing indentation. + + ### Line Length and Long Strings Maximum line length is 80 characters. @@ -263,6 +345,8 @@ long_string="I am an exceptionally long string." ``` + + ### Pipelines Pipelines should be split one per line if they don't all fit on one @@ -286,6 +370,8 @@ command1 \ | command4 ``` + + ### Loops Put `; do` and `; then` on the same line as the @@ -320,6 +406,8 @@ for dir in "${dirs_to_cleanup[@]}"; do done ``` + + ### Case statement * Indent alternatives by 2 spaces. @@ -374,6 +462,8 @@ while getopts 'abf:v' flag; do done ``` + + ### Variable expansion In order of precedence: Stay consistent with what you find; quote your @@ -430,6 +520,8 @@ They are listed in order of precedence. NOTE: Using braces in `${var}` is *not* a form of quoting. "Double quotes" must be used *as well*. + + ### Quoting * Always quote strings containing variables, command substitutions, spaces or @@ -516,14 +608,22 @@ grep -cP '([Ss]pecial|\|?characters*)$' ${1:+"$1"} (set -- 1 "2 two" "3 three tres"; echo $#; set -- "$@"; echo "$#, $@") ``` + + ## Features and Bugs + + + + ### ShellCheck The [ShellCheck project](https://www.shellcheck.net/) identifies common bugs and warnings for your shell scripts. It is recommended for all scripts, large or small. + + ### Command Substitution Use `$(command)` instead of backticks. @@ -544,6 +644,8 @@ var="$(command "$(command1)")" var="`command \`command1\``" ``` + + ### Test, `[ … ]`, and `[[ … ]]` @@ -578,6 +680,8 @@ fi For the gory details, see E14 at http://tiswww.case.edu/php/chet/bash/FAQ + + ### Testing Strings Use quotes rather than filler characters where possible. @@ -664,6 +768,8 @@ if [[ "${my_var}" > 3 ]]; then fi ``` + + ### Wildcard Expansion of Filenames Use an explicit path when doing wildcard expansion of filenames. @@ -690,6 +796,8 @@ rm: cannot remove `./somedir': Is a directory removed `./somefile' ``` + + ### Eval `eval` should be avoided. @@ -707,6 +815,8 @@ eval $(set_my_variables) variable="$(eval some_function)" ``` + + ### Arrays Bash arrays should be used to store lists of elements, to avoid quoting @@ -747,6 +857,8 @@ declare -a files=($(ls /directory)) mybinary $(get_arguments) ``` + + #### Arrays Pros * Using Arrays allows lists of things without confusing quoting @@ -755,10 +867,14 @@ mybinary $(get_arguments) * Arrays make it possible to safely store sequences/lists of arbitrary strings, including strings containing whitespace. + + #### Arrays Cons Using arrays can risk a script’s complexity growing. + + #### Arrays Decision Arrays should be used to safely create and pass around lists. In @@ -768,6 +884,8 @@ avoid confusing quoting issues. Use quoted expansion – advanced data manipulation is required, shell scripting should be avoided altogether; see [above](#when-to-use-shell). + + ### Pipes to While Use process substitution or for loops in preference to piping to while. @@ -833,6 +951,8 @@ while read src dest type opts rest; do done < /proc/mounts ``` + + ### Arithmetic Always use `(( … ))` or `$(( … ))` rather than @@ -915,9 +1035,12 @@ sec=30 echo $(( hr * 3600 + min * 60 + sec )) # prints 7530 as expected ``` + ## Naming Conventions + + ### Function Names Lower-case, with underscores to separate words. Separate libraries with @@ -947,6 +1070,8 @@ The `function` keyword is extraneous when "()" is present after the function name, but enhances quick identification of functions. + + ### Variable Names As for function names. @@ -960,6 +1085,8 @@ for zone in "${zones[@]}"; do done ``` + + ### Constants and Environment Variable Names All caps, separated with underscores, declared at the top of the file. @@ -991,6 +1118,8 @@ done readonly VERBOSE ``` + + ### Source Filenames Lowercase, with underscores to separate words if desired. @@ -999,6 +1128,8 @@ This is for consistency with other code styles in Google: `maketemplate` or `make_template` but not `make-template`. + + ### Read-only Variables Use `readonly` or `declare -r` to ensure they're @@ -1017,6 +1148,8 @@ else fi ``` + + ### Use Local Variables Declare function-specific variables with `local`. Declaration @@ -1056,6 +1189,8 @@ my_func2() { } ``` + + ### Function Location Put all functions together in the file just below constants. Don't hide @@ -1068,6 +1203,8 @@ may be done before declaring functions. Don't hide executable code between functions. Doing so makes the code difficult to follow and results in nasty surprises when debugging. + + ### main A function called `main` is required for scripts long enough @@ -1087,9 +1224,12 @@ main "$@" Obviously, for short scripts where it's just a linear flow, `main` is overkill and so is not required. + ## Calling Commands + + ### Checking Return Values Always check return values and give informative return values. @@ -1143,6 +1283,8 @@ if (( return_codes[1] != 0 )); then fi ``` + + ### Builtin Commands vs. External Commands Given the choice between invoking a shell builtin and invoking a @@ -1166,6 +1308,7 @@ addition="$(expr ${X} + ${Y})" substitution="$(echo "${string}" | sed -e 's/^foo/bar/')" ``` + ## Conclusion