| <?xml version="1.0"?> |
| <?xml-stylesheet type="text/xsl" href="styleguide.xsl"?> |
| <GUIDE title="Shell Style Guide"> |
| |
| <p align="right"> |
| |
| Revision 1.26 |
| </p> |
| |
| |
| <address> |
| Paul Armstrong<br/> |
| Too many more to mention<br/> |
| </address> |
| |
| <OVERVIEW> |
| |
| <CATEGORY title="Background"> |
| |
| |
| |
| <STYLEPOINT title="Which Shell to Use"> |
| <SUMMARY> |
| <code>Bash</code> is the only shell scripting language permitted for |
| executables. |
| </SUMMARY> |
| <BODY> |
| <p> |
| Executables must start with <code>#!/bin/bash</code> and a minimum |
| number of flags. Use <code>set</code> to set shell options so that |
| calling your script as <code>bash <i><script_name></i></code> |
| does not break its functionality. |
| </p> |
| <p> |
| Restricting all executable shell scripts to <b>bash</b> |
| gives us a consistent shell language that's installed on all our |
| machines. |
| </p> |
| <p> |
| 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. |
| </p> |
| </BODY> |
| </STYLEPOINT> |
| |
| <STYLEPOINT title="When to use Shell"> |
| <SUMMARY> |
| Shell should only be used for small utilities or simple wrapper |
| scripts. |
| </SUMMARY> |
| <BODY> |
| <p> |
| While shell scripting isn't a development language, it is used for |
| writing various utility scripts throughout Google. This |
| style guide is more a recognition of its use rather than |
| a suggestion that it be used for widespread deployment. |
| </p> |
| <p> |
| Some guidelines: |
| <ul> |
| <li> |
| If you're mostly calling other utilities and are doing relatively |
| little data manipulation, shell is an acceptable choice for the |
| task. |
| </li> |
| <li> |
| If performance matters, use something other than shell. |
| </li> |
| <li> |
| If you find you need to use arrays for anything more than |
| assignment of <code>${PIPESTATUS}</code>, you should use Python. |
| </li> |
| <li> |
| If you are writing a script that is more than 100 lines long, you |
| should probably be writing it in Python instead. Bear in mind |
| that scripts grow. Rewrite your script in another language |
| early to avoid a time-consuming rewrite at a later date. |
| </li> |
| </ul> |
| </p> |
| </BODY> |
| </STYLEPOINT> |
| </CATEGORY> |
| |
| </OVERVIEW> |
| |
| <CATEGORY title="Shell Files and Interpreter Invocation"> |
| |
| <STYLEPOINT title="File Extensions"> |
| <SUMMARY> |
| Executables should have no extension (strongly preferred) or a |
| <code>.sh</code> extension. |
| Libraries must have a <code>.sh</code> extension and should not be |
| executable. |
| </SUMMARY> |
| <BODY> |
| <p> |
| It is not necessary to know what language a program is written in when |
| executing it and shell doesn't require an extension so we prefer not to |
| use one for executables. |
| </p> |
| <p> |
| However, for libraries it's important to know what language it is and |
| sometimes there's a need to have similar libraries in different |
| languages. This allows library files with identical purposes but |
| different languages to be identically named except for the |
| language-specific suffix. |
| </p> |
| </BODY> |
| </STYLEPOINT> |
| |
| <STYLEPOINT title="SUID/SGID"> |
| <SUMMARY> |
| SUID and SGID are <em>forbidden</em> on shell scripts. |
| </SUMMARY> |
| <BODY> |
| <p> |
| There are too many security issues with shell that make it nearly |
| impossible to secure sufficiently to allow SUID/SGID. While bash does |
| make it difficult to run SUID, it's still possible on some platforms |
| which is why we're being explicit about banning it. |
| </p> |
| <p> |
| Use <code>sudo</code> to provide elevated access if you need it. |
| </p> |
| </BODY> |
| </STYLEPOINT> |
| |
| </CATEGORY> |
| |
| <CATEGORY title="Environment"> |
| |
| <STYLEPOINT title="STDOUT vs STDERR"> |
| <SUMMARY> |
| All error messages should go to <code>STDERR</code>. |
| </SUMMARY> |
| <BODY> |
| <p> |
| This makes it easier |
| to separate normal status from actual issues. |
| </p> |
| <p> |
| A function to print out error messages along with other status |
| information is recommended. |
| <CODE_SNIPPET> |
| err() { |
| echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $@" >&2 |
| } |
| |
| if ! do_something; then |
| err "Unable to do_something" |
| exit "${E_DID_NOTHING}" |
| fi |
| </CODE_SNIPPET> |
| </p> |
| </BODY> |
| </STYLEPOINT> |
| |
| </CATEGORY> |
| |
| <CATEGORY title="Comments"> |
| |
| <STYLEPOINT title="File Header"> |
| <SUMMARY> |
| Start each file with a description of its contents. |
| </SUMMARY> |
| <BODY> |
| <p> |
| Every file must have a top-level comment including a brief overview of |
| its contents. |
| A |
| copyright notice |
| and author information are optional. |
| </p> |
| <p> |
| Example: |
| <CODE_SNIPPET> |
| #!/bin/bash |
| # |
| # Perform hot backups of Oracle databases. |
| </CODE_SNIPPET> |
| </p> |
| |
| |
| </BODY> |
| </STYLEPOINT> |
| |
| <STYLEPOINT title="Function Comments"> |
| <SUMMARY> |
| Any function that is not both obvious and short must be commented. Any |
| function in a library must be commented regardless of length or |
| complexity. |
| </SUMMARY> |
| <BODY> |
| <p> |
| It should be possible for someone else to learn how to use your |
| program or to use a function in your library by reading the comments |
| (and self-help, if provided) without reading the code. |
| </p> |
| <p> |
| All function comments should contain: |
| <ul> |
| <li> |
| Description of the function |
| </li> |
| <li> |
| Global variables used and modified |
| </li> |
| <li> |
| Arguments taken |
| </li> |
| <li> |
| Returned values other than the default exit status of the last |
| command run |
| </li> |
| </ul> |
| </p> |
| <p> |
| Example: |
| <CODE_SNIPPET> |
| #!/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() { |
| ... |
| } |
| </CODE_SNIPPET> |
| </p> |
| </BODY> |
| </STYLEPOINT> |
| |
| <STYLEPOINT title="Implementation Comments"> |
| <SUMMARY> |
| Comment tricky, non-obvious, interesting or important parts of your code. |
| </SUMMARY> |
| <BODY> |
| <p> |
| 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. |
| </p> |
| </BODY> |
| </STYLEPOINT> |
| |
| <STYLEPOINT title="TODO Comments"> |
| <SUMMARY> |
| Use TODO comments for code that is temporary, a short-term solution, or |
| good-enough but not perfect. |
| </SUMMARY> |
| <BODY> |
| <p> |
| This matches the convention in the <a href="cppguide.xml?showone=TODO_Comments#TODO_Comments">C++ |
| Guide</a>. |
| </p> |
| <p> |
| TODOs should include the string TODO in all caps, followed by your |
| username in parentheses. A colon is optional. It's preferable to put a |
| bug/ticket number next to the TODO item as well. |
| </p> |
| <p> |
| Examples: |
| |
| <CODE_SNIPPET> |
| # TODO(mrmonkey): Handle the unlikely edge cases (bug ####) |
| </CODE_SNIPPET> |
| </p> |
| </BODY> |
| </STYLEPOINT> |
| |
| </CATEGORY> |
| |
| <CATEGORY title="Formatting"> |
| <p> |
| While you should follow the style that's already there for files that |
| you're modifying, the following are required for any new code. |
| </p> |
| |
| <STYLEPOINT title="Indentation"> |
| <SUMMARY> |
| Indent 2 spaces. No tabs. |
| </SUMMARY> |
| <BODY> |
| <p> |
| 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. |
| </p> |
| </BODY> |
| </STYLEPOINT> |
| |
| <STYLEPOINT title="Line Length and Long Strings"> |
| <SUMMARY> |
| Maximum line length is 80 characters. |
| </SUMMARY> |
| <BODY> |
| <p> |
| If you have to write strings that are longer than 80 characters, this |
| should be done with a here document or an embedded newline if possible. |
| Literal strings that have to be longer than 80 chars and can't sensibly |
| be split are ok, but it's strongly preferred to find a way to make it |
| shorter. |
| </p> |
| <p> |
| <CODE_SNIPPET> |
| # 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." |
| </CODE_SNIPPET> |
| </p> |
| </BODY> |
| </STYLEPOINT> |
| |
| <STYLEPOINT title="Pipelines"> |
| <SUMMARY> |
| Pipelines should be split one per line if they don't all fit on one line. |
| </SUMMARY> |
| <BODY> |
| <p> |
| If a pipeline all fits on one line, it should be on one line. |
| </p> |
| <p> |
| If not, it should be split at one pipe segment per line with the pipe |
| on the newline and a 2 space indent for the next section of the pipe. |
| This applies to a chain of commands combined using '|' as well as to |
| logical compounds using '||' and '&&'. |
| <CODE_SNIPPET> |
| # All fits on one line |
| command1 | command2 |
| |
| # Long commands |
| command1 \ |
| | command2 \ |
| | command3 \ |
| | command4 |
| </CODE_SNIPPET> |
| </p> |
| </BODY> |
| </STYLEPOINT> |
| |
| <STYLEPOINT title="Loops"> |
| <SUMMARY> |
| Put <code>; do</code> and <code>; then</code> on the same line as the |
| <code>while</code>, <code>for</code> or <code>if</code>. |
| </SUMMARY> |
| <BODY> |
| <p> |
| Loops in shell are a bit different, but we follow the same principles |
| as with braces when declaring functions. That is: <code>; then</code> |
| and <code>; do</code> should be on the same line as the if/for/while. |
| <code>else</code> should be on its own line and closing statements |
| should be on their own line vertically aligned with the opening |
| statement. |
| </p> |
| <p> |
| Example: |
| <CODE_SNIPPET> |
| 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 |
| </CODE_SNIPPET> |
| </p> |
| </BODY> |
| </STYLEPOINT> |
| |
| <STYLEPOINT title="Case statement"> |
| <SUMMARY> |
| <ul> |
| <li> |
| Indent alternatives by 2 spaces. |
| </li> |
| <li> |
| A one-line alternative needs a space after the close parenthesis of |
| the pattern and before the <code>;;</code>. |
| </li> |
| <li> |
| Long or multi-command alternatives should be split over multiple |
| lines with the pattern, actions, and <code>;;</code> on separate |
| lines. |
| </li> |
| </ul> |
| </SUMMARY> |
| <BODY> |
| <p> |
| The matching expressions are indented one level from the 'case' and |
| 'esac'. Multiline actions are indented another level. In general, |
| there is no need to quote match expressions. Pattern expressions |
| should not be preceded by an open parenthesis. Avoid the |
| <code>;&</code> and <code>;;&</code> notations. |
| </p> |
| <CODE_SNIPPET> |
| case "${expression}" in |
| a) |
| variable="..." |
| some_command "${variable}" "${other_expr}" ... |
| ;; |
| absolute) |
| actions="relative" |
| another_command "${actions}" "${other_expr}" ... |
| ;; |
| *) |
| error "Unexpected expression '${expression}'" |
| ;; |
| esac |
| </CODE_SNIPPET> |
| <p> |
| Simple commands may be put on the same line as the pattern <i>and</i> |
| <code>;;</code> as long as the expression remains readable. This is |
| often appropriate for single-letter option processing. When the |
| actions don't fit on a single line, put the pattern on a line on its |
| own, then the actions, then <code>;;</code> also on a line of its own. |
| When on the same line as the actions, use a space after the close |
| parenthesis of the pattern and another before the <code>;;</code>. |
| </p> |
| <CODE_SNIPPET> |
| 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 |
| </CODE_SNIPPET> |
| </BODY> |
| </STYLEPOINT> |
| |
| <STYLEPOINT title="Variable expansion"> |
| <SUMMARY> |
| In order of precedence: Stay consistent with what you find; |
| quote your variables; |
| prefer "${var}" over "$var", but see details. |
| </SUMMARY> |
| <BODY> |
| <p> |
| These are meant to be guidelines, as the topic seems too controversial for |
| a mandatory regulation. |
| <br/> |
| They are listed in order of precedence. |
| </p> |
| <ol> |
| <li> |
| Stay consistent with what you find for existing code. |
| </li> |
| <li> |
| Quote variables, see <a href="#Quoting">Quoting section below</a>. |
| </li> |
| <li> |
| <p> |
| Don't brace-quote single character shell |
| specials / positional parameters, unless strictly necessary |
| or avoiding deep confusion. |
| <br/> |
| Prefer brace-quoting all other variables. |
| <CODE_SNIPPET> |
| # Section of <em>recommended</em> 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 <em>discouraged</em> 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" |
| </CODE_SNIPPET> |
| </p> |
| </li> |
| </ol> |
| </BODY> |
| </STYLEPOINT> |
| |
| <STYLEPOINT title="Quoting"> |
| <SUMMARY> |
| <ul> |
| <li> |
| Always quote strings containing variables, command substitutions, |
| spaces or shell meta characters, unless careful unquoted expansion |
| is required. |
| </li> |
| <li> |
| Prefer quoting strings that are "words" |
| (as opposed to command options or path names). |
| </li> |
| <li> |
| Never quote <em>literal</em> integers. |
| </li> |
| <li> |
| Be aware of the quoting rules for |
| <a href="#Test,_%5B_and_%5B%5B">pattern matches in [[</a>. |
| </li> |
| <li> |
| Use "$@" unless you have a specific reason to use $*. |
| </li> |
| </ul> |
| </SUMMARY> |
| <BODY> |
| <p> |
| <CODE_SNIPPET> |
| # '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 "$#, $@") |
| </CODE_SNIPPET> |
| </p> |
| </BODY> |
| </STYLEPOINT> |
| |
| </CATEGORY> |
| |
| <CATEGORY title="Features and Bugs"> |
| |
| <STYLEPOINT title="Command Substitution"> |
| <SUMMARY> |
| Use <code>$(command)</code> instead of backticks. |
| </SUMMARY> |
| <BODY> |
| <p> |
| Nested backticks require escaping the inner ones with <code>\</code>. |
| The <code>$(command)</code> format doesn't change when nested and is |
| easier to read. |
| </p> |
| <p> |
| Example: |
| <CODE_SNIPPET> |
| # This is preferred: |
| var="$(command "$(command1)")" |
| |
| # This is not: |
| var="`command \`command1\``" |
| </CODE_SNIPPET> |
| </p> |
| </BODY> |
| </STYLEPOINT> |
| |
| <STYLEPOINT title="Test, [ and [["> |
| <SUMMARY> |
| <code>[[ ... ]]</code> is preferred over <code>[</code>, |
| <code>test</code> and <code>/usr/bin/[</code>. |
| </SUMMARY> |
| <BODY> |
| <p> |
| <code>[[ ... ]]</code> reduces errors as no pathname expansion or word |
| splitting takes place between <code>[[</code> and <code>]]</code> and |
| <code>[[ ... ]]</code> allows for regular expression matching where |
| <code>[ ... ]</code> does not. |
| <CODE_SNIPPET> |
| # 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 |
| </CODE_SNIPPET> |
| </p> |
| </BODY> |
| </STYLEPOINT> |
| |
| <STYLEPOINT title="Testing Strings"> |
| <SUMMARY> |
| Use quotes rather than filler characters where possible. |
| </SUMMARY> |
| <BODY> |
| <p> |
| Bash is smart enough to deal with an empty string in a test. So, given |
| that the code is much easier to read, use tests for empty/non-empty |
| strings or empty strings rather than filler characters. |
| <CODE_SNIPPET> |
| # 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 |
| </CODE_SNIPPET> |
| </p> |
| <p> |
| To avoid confusion about what you're testing for, explicitly use |
| <code>-z</code> or <code>-n</code>. |
| <CODE_SNIPPET> |
| # 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 |
| </CODE_SNIPPET> |
| </p> |
| </BODY> |
| </STYLEPOINT> |
| |
| <STYLEPOINT title="Wildcard Expansion of Filenames"> |
| <SUMMARY> |
| Use an explicit path when doing wildcard expansion of filenames. |
| </SUMMARY> |
| <BODY> |
| <p> |
| As filenames can begin with a <code>-</code>, it's a lot safer to |
| expand wildcards with <code>./*</code> instead of <code>*</code>. |
| <CODE_SNIPPET> |
| # 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' |
| </CODE_SNIPPET> |
| </p> |
| </BODY> |
| </STYLEPOINT> |
| |
| <STYLEPOINT title="Eval"> |
| <SUMMARY> |
| <code>eval</code> should be avoided. |
| </SUMMARY> |
| <BODY> |
| <p> |
| Eval munges the input when used for assignment to variables and can set |
| variables without making it possible to check what those variables |
| were. |
| <CODE_SNIPPET> |
| # 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)" |
| </CODE_SNIPPET> |
| </p> |
| </BODY> |
| </STYLEPOINT> |
| |
| <STYLEPOINT title="Pipes to While"> |
| <SUMMARY> |
| Use process substitution or for loops in preference to piping to while. |
| Variables modified in a while loop do not propagate to the parent |
| because the loop's commands run in a subshell. |
| </SUMMARY> |
| <BODY> |
| <p> |
| The implicit subshell in a pipe to while can make it difficult to track |
| down bugs. |
| <BAD_CODE_SNIPPET> |
| last_line='NULL' |
| your_command | while read line; do |
| last_line="${line}" |
| done |
| |
| # This will output 'NULL' |
| echo "${last_line}" |
| </BAD_CODE_SNIPPET> |
| </p> |
| <p> |
| Use a for loop if you are confident that the input will not contain |
| spaces or special characters (usually, this means not user input). |
| <CODE_SNIPPET> |
| total=0 |
| # Only do this if there are no spaces in return values. |
| for value in $(command); do |
| total+="${value}" |
| done |
| </CODE_SNIPPET> |
| </p> |
| <p> |
| Using process substitution allows redirecting output but puts the |
| commands in an explicit subshell rather than the implicit subshell that |
| bash creates for the while loop. |
| <CODE_SNIPPET> |
| 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}" |
| </CODE_SNIPPET> |
| </p> |
| <p> |
| Use while loops where it is not necessary to pass complex results |
| to the parent shell - this is typically where some more complex |
| "parsing" is required. Beware that simple examples are probably |
| more easily done with a tool such as awk. This may also be useful |
| where you specifically don't want to change the parent scope variables. |
| <CODE_SNIPPET> |
| # 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 |
| </CODE_SNIPPET> |
| </p> |
| </BODY> |
| </STYLEPOINT> |
| |
| </CATEGORY> |
| |
| <CATEGORY title="Naming Conventions"> |
| |
| <STYLEPOINT title="Function Names"> |
| <SUMMARY> |
| Lower-case, with underscores to separate words. Separate libraries |
| with <code>::</code>. Parentheses are required after the function name. |
| The keyword <code>function</code> is optional, but must be used |
| consistently throughout a project. |
| </SUMMARY> |
| <BODY> |
| <p> |
| If you're writing single functions, use lowercase and separate words |
| with underscore. If you're writing a package, separate package names |
| with <code>::</code>. Braces must be on the same line as the function |
| name (as with other languages at Google) and no space between the |
| function name and the parenthesis. |
| <CODE_SNIPPET> |
| # Single function |
| my_func() { |
| ... |
| } |
| |
| # Part of a package |
| mypackage::my_func() { |
| ... |
| } |
| </CODE_SNIPPET> |
| </p> |
| <p> |
| The <code>function</code> keyword is extraneous when "()" is present |
| after the function name, but enhances quick identification of |
| functions. |
| </p> |
| </BODY> |
| </STYLEPOINT> |
| |
| <STYLEPOINT title="Variable Names"> |
| <SUMMARY> |
| As for function names. |
| </SUMMARY> |
| <BODY> |
| <p> |
| Variables names for loops should be similarly named for any variable |
| you're looping through. |
| <CODE_SNIPPET> |
| for zone in ${zones}; do |
| something_with "${zone}" |
| done |
| </CODE_SNIPPET> |
| </p> |
| </BODY> |
| </STYLEPOINT> |
| |
| <STYLEPOINT title="Constants and Environment Variable Names"> |
| <SUMMARY> |
| All caps, separated with underscores, declared at the top of the file. |
| </SUMMARY> |
| <BODY> |
| <p> |
| Constants and anything exported to the environment should be |
| capitalized. |
| <CODE_SNIPPET> |
| # Constant |
| readonly PATH_TO_FILES='/some/path' |
| |
| # Both constant and environment |
| declare -xr ORACLE_SID='PROD' |
| </CODE_SNIPPET> |
| </p> |
| <p> |
| Some things become constant at their first setting (for example, via |
| getopts). Thus, it's OK to set a constant in getopts or based on a |
| condition, but it should be made readonly immediately afterwards. |
| Note that <code>declare</code> doesn't operate on global variables |
| within functions, so <code>readonly</code> or <code>export</code> is |
| recommended instead. |
| </p> |
| <CODE_SNIPPET> |
| VERBOSE='false' |
| while getopts 'v' flag; do |
| case "${flag}" in |
| v) VERBOSE='true' ;; |
| esac |
| done |
| readonly VERBOSE |
| </CODE_SNIPPET> |
| </BODY> |
| </STYLEPOINT> |
| |
| <STYLEPOINT title="Source Filenames"> |
| <SUMMARY> |
| Lowercase, with underscores to separate words if desired. |
| </SUMMARY> |
| <BODY> |
| <p> |
| This is for consistency with other code styles in Google: |
| <code>maketemplate</code> or <code>make_template</code> but not |
| <code>make-template</code>. |
| </p> |
| </BODY> |
| </STYLEPOINT> |
| |
| <STYLEPOINT title="Read-only Variables"> |
| <SUMMARY> |
| Use <code>readonly</code> or <code>declare -r</code> to ensure they're |
| read only. |
| </SUMMARY> |
| <BODY> |
| <p> |
| As globals are widely used in shell, it's important to catch errors |
| when working with them. When you declare a variable that is |
| meant to be read-only, make this explicit. |
| <CODE_SNIPPET> |
| zip_version="$(dpkg --status zip | grep Version: | cut -d ' ' -f 2)" |
| if [[ -z "${zip_version}" ]]; then |
| error_message |
| else |
| readonly zip_version |
| fi |
| </CODE_SNIPPET> |
| </p> |
| </BODY> |
| </STYLEPOINT> |
| |
| <STYLEPOINT title="Use Local Variables"> |
| <SUMMARY> |
| Declare function-specific variables with <code>local</code>. Declaration |
| and assignment should be on different lines. |
| </SUMMARY> |
| <BODY> |
| <p> |
| Ensure that local variables are only seen inside a function and its |
| children by using <code>local</code> when declaring them. This avoids |
| polluting the global name space and inadvertently setting variables |
| that may have significance outside the function. |
| </p> |
| <p> |
| Declaration and assignment must be separate statements when |
| the assignment value is provided by a command substitution; as |
| the 'local' builtin does not propagate the exit code from the |
| command substitution. |
| <CODE_SNIPPET> |
| my_func2() { |
| local name="$1" |
| |
| # Separate lines for declaration and assignment: |
| local my_var |
| my_var="$(my_func)" || return |
| |
| # DO NOT do this: $? contains the exit code of 'local', not my_func |
| local my_var="$(my_func)" |
| [[ $? -eq 0 ]] || return |
| |
| ... |
| } |
| </CODE_SNIPPET> |
| </p> |
| </BODY> |
| </STYLEPOINT> |
| |
| <STYLEPOINT title="Function Location"> |
| <SUMMARY> |
| Put all functions together in the file just below constants. Don't hide |
| executable code between functions. |
| </SUMMARY> |
| <BODY> |
| <p> |
| If you've got functions, put them all together near the top of the |
| file. Only includes, <code>set</code> statements and setting constants |
| may be done before declaring functions. |
| </p> |
| <p> |
| Don't hide executable code between functions. Doing so makes the code |
| difficult to follow and results in nasty surprises when debugging. |
| </p> |
| </BODY> |
| </STYLEPOINT> |
| |
| <STYLEPOINT title="main"> |
| <SUMMARY> |
| A function called <code>main</code> is required for scripts long enough |
| to contain at least one other function. |
| </SUMMARY> |
| <BODY> |
| <p> |
| In order to easily find the start of the program, put the main |
| program in a function called <code>main</code> as the bottom most |
| function. This provides consistency with the rest of the code base as |
| well as allowing you to define more variables as <code>local</code> |
| (which can't be done if the main code is not a function). The last |
| non-comment line in the file should be a call to <code>main</code>: |
| <CODE_SNIPPET> |
| main "$@" |
| </CODE_SNIPPET> |
| </p> |
| <p> |
| Obviously, for short scripts where it's just a linear flow, |
| <code>main</code> is overkill and so is not required. |
| </p> |
| </BODY> |
| </STYLEPOINT> |
| |
| </CATEGORY> |
| |
| <CATEGORY title="Calling Commands"> |
| |
| <STYLEPOINT title="Checking Return Values"> |
| <SUMMARY> |
| Always check return values and give informative return values. |
| </SUMMARY> |
| <BODY> |
| <p> |
| For unpiped commands, use <code>$?</code> or check directly via an |
| <code>if</code> statement to keep it simple. |
| </p> |
| <p> |
| Example: |
| <CODE_SNIPPET> |
| if ! mv "${file_list}" "${dest_dir}/" ; then |
| echo "Unable to move ${file_list} to ${dest_dir}" >&2 |
| exit "${E_BAD_MOVE}" |
| fi |
| |
| # Or |
| mv "${file_list}" "${dest_dir}/" |
| if [[ "$?" -ne 0 ]]; then |
| echo "Unable to move ${file_list} to ${dest_dir}" >&2 |
| exit "${E_BAD_MOVE}" |
| fi |
| |
| </CODE_SNIPPET> |
| </p> |
| <p> |
| Bash also has the <code>PIPESTATUS</code> variable that allows checking |
| of the return code from all parts of a pipe. If it's only necessary to |
| check success or failure of the whole pipe, then the following is |
| acceptable: |
| <CODE_SNIPPET> |
| tar -cf - ./* | ( cd "${dir}" && tar -xf - ) |
| if [[ "${PIPESTATUS[0]}" -ne 0 || "${PIPESTATUS[1]}" -ne 0 ]]; then |
| echo "Unable to tar files to ${dir}" >&2 |
| fi |
| </CODE_SNIPPET> |
| </p> |
| <p> |
| However, as <code>PIPESTATUS</code> will be overwritten as soon as you |
| do any other command, if you need to act differently on errors based on |
| where it happened in the pipe, you'll need to assign |
| <code>PIPESTATUS</code> to another variable immediately after running |
| the command (don't forget that <code>[</code> is a command and will |
| wipe out <code>PIPESTATUS</code>). |
| <CODE_SNIPPET> |
| tar -cf - ./* | ( cd "${DIR}" && tar -xf - ) |
| return_codes=(${PIPESTATUS[*]}) |
| if [[ "${return_codes[0]}" -ne 0 ]]; then |
| do_something |
| fi |
| if [[ "${return_codes[1]}" -ne 0 ]]; then |
| do_something_else |
| fi |
| </CODE_SNIPPET> |
| </p> |
| </BODY> |
| </STYLEPOINT> |
| |
| <STYLEPOINT title="Builtin Commands vs. External Commands"> |
| <SUMMARY> |
| Given the choice between invoking a shell builtin and invoking a separate |
| process, choose the builtin. |
| </SUMMARY> |
| <BODY> |
| <p> |
| We prefer the use of builtins such as the <em>Parameter Expansion</em> |
| functions in <code>bash(1)</code> as it's more robust and portable |
| (especially when compared to things like sed). |
| </p> |
| <p> |
| Example: |
| <CODE_SNIPPET> |
| # Prefer this: |
| addition=$((${X} + ${Y})) |
| substitution="${string/#foo/bar}" |
| |
| # Instead of this: |
| addition="$(expr ${X} + ${Y})" |
| substitution="$(echo "${string}" | sed -e 's/^foo/bar/')" |
| </CODE_SNIPPET> |
| </p> |
| </BODY> |
| </STYLEPOINT> |
| |
| </CATEGORY> |
| |
| <CATEGORY title="Conclusion"> |
| <p> |
| Use common sense and <em>BE CONSISTENT</em>. |
| </p> |
| <p> |
| Please take a few minutes to read the Parting Words section at the bottom |
| of the <a href="cppguide.xml">C++ Guide</a>. |
| </p> |
| </CATEGORY> |
| |
| <p align="right"> |
| Revision 1.26 |
| </p> |
| |
| </GUIDE> |