From a0977d221425a5c95738521ab4e56550d08a147e Mon Sep 17 00:00:00 2001
From: Isaac Good
+The guide has been modes to shellguide.html.
-Revision 1.26
-
- Executables must start with
- Restricting all executable shell scripts to bash
- gives us a consistent shell language that's installed on all our
- machines.
-
- 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.
-
- 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.
-
- Some guidelines:
-
- Too many more to mention
-
-
-Bash
is the only shell scripting language permitted for
- executables.
- #!/bin/bash
and a minimum
- number of flags. Use set
to set shell options so that
- calling your script as bash <script_name>
- does not break its functionality.
-
-
- ${PIPESTATUS}
, you should use Python.
-
.sh
extension.
- Libraries must have a .sh
extension and should not be
- executable.
- - 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. -
-- 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. -
- -- 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. -
-
- Use sudo
to provide elevated access if you need it.
-
STDERR
.
- - This makes it easier - to separate normal status from actual issues. -
-
- A function to print out error messages along with other status
- information is recommended.
-
- Every file must have a top-level comment including a brief overview of - its contents. - A - copyright notice - and author information are optional. -
-
- Example:
-
- 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. -
-- All function comments should contain: -
- Example:
-
- 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. -
- -- This matches the convention in the C++ - Guide. -
-- 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. -
-
- Examples:
-
-
- While you should follow the style that's already there for files that - you're modifying, the following are required for any new code. -
- -- 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. -
- -- 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. -
-
-
- If a pipeline all fits on one line, it should be on one line. -
-
- 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 '&&'.
-
; do
and ; then
on the same line as the
- while
, for
or if
.
-
- Loops in shell are a bit different, but we follow the same principles
- as with braces when declaring functions. That is: ; then
- and ; do
should be on the same line as the if/for/while.
- else
should be on its own line and closing statements
- should be on their own line vertically aligned with the opening
- statement.
-
- Example:
-
;;
.
- ;;
on separate
- lines.
-
- 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
- ;&
and ;;&
notations.
-
- Simple commands may be put on the same line as the pattern and
- ;;
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 ;;
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 ;;
.
-
- These are meant to be guidelines, as the topic seems too controversial for
- a mandatory regulation.
-
- They are listed in order of precedence.
-
- Don't brace-quote single character shell
- specials / positional parameters, unless strictly necessary
- or avoiding deep confusion.
-
- Prefer brace-quoting all other variables.
-
-
$(command)
instead of backticks.
-
- Nested backticks require escaping the inner ones with \
.
- The $(command)
format doesn't change when nested and is
- easier to read.
-
- Example:
-
[[ ... ]]
is preferred over [
,
- test
and /usr/bin/[
.
-
- [[ ... ]]
reduces errors as no pathname expansion or word
- splitting takes place between [[
and ]]
and
- [[ ... ]]
allows for regular expression matching where
- [ ... ]
does not.
-
- 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.
-
- To avoid confusion about what you're testing for, explicitly use
- -z
or -n
.
-
- As filenames can begin with a -
, it's a lot safer to
- expand wildcards with ./*
instead of *
.
-
eval
should be avoided.
-
- Eval munges the input when used for assignment to variables and can set
- variables without making it possible to check what those variables
- were.
-
- The implicit subshell in a pipe to while can make it difficult to track
- down bugs.
-
- Use a for loop if you are confident that the input will not contain
- spaces or special characters (usually, this means not user input).
-
- 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.
-
- 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.
-
::
. Parentheses are required after the function name.
- The keyword function
is optional, but must be used
- consistently throughout a project.
-
- If you're writing single functions, use lowercase and separate words
- with underscore. If you're writing a package, separate package names
- with ::
. 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.
-
- The function
keyword is extraneous when "()" is present
- after the function name, but enhances quick identification of
- functions.
-
- Variables names for loops should be similarly named for any variable
- you're looping through.
-
- Constants and anything exported to the environment should be
- capitalized.
-
- 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 declare
doesn't operate on global variables
- within functions, so readonly
or export
is
- recommended instead.
-
- This is for consistency with other code styles in Google:
- maketemplate
or make_template
but not
- make-template
.
-
readonly
or declare -r
to ensure they're
- read only.
-
- 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.
-
local
. Declaration
- and assignment should be on different lines.
-
- Ensure that local variables are only seen inside a function and its
- children by using local
when declaring them. This avoids
- polluting the global name space and inadvertently setting variables
- that may have significance outside the function.
-
- 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.
-
- If you've got functions, put them all together near the top of the
- file. Only includes, set
statements and setting constants
- 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
is required for scripts long enough
- to contain at least one other function.
-
- In order to easily find the start of the program, put the main
- program in a function called main
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 local
- (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 main
:
-
- Obviously, for short scripts where it's just a linear flow,
- main
is overkill and so is not required.
-
- For unpiped commands, use $?
or check directly via an
- if
statement to keep it simple.
-
- Example:
-
- Bash also has the PIPESTATUS
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:
-
- However, as PIPESTATUS
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
- PIPESTATUS
to another variable immediately after running
- the command (don't forget that [
is a command and will
- wipe out PIPESTATUS
).
-
- We prefer the use of builtins such as the Parameter Expansion
- functions in bash(1)
as it's more robust and portable
- (especially when compared to things like sed).
-
- Example:
-
- Use common sense and BE CONSISTENT. -
-- Please take a few minutes to read the Parting Words section at the bottom - of the C++ Guide. -
--Revision 1.26 -
- -