You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1152 lines
35 KiB
1152 lines
35 KiB
<?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.html?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 https://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.html">C++ Guide</a>. |
|
</p> |
|
</CATEGORY> |
|
|
|
<p align="right"> |
|
Revision 1.26 |
|
</p> |
|
|
|
</GUIDE>
|
|
|