└── README.md /README.md: -------------------------------------------------------------------------------- 1 | # progrium/bashstyle 2 | 3 | Bash is the JavaScript of systems programming. Although in some cases it's better to use a systems language like C or Go, Bash is an ideal systems language for smaller POSIX-oriented or command line tasks. Here's three quick reasons why: 4 | 5 | * It's everywhere. Like JavaScript for the web, Bash is already there ready for systems programming. 6 | * It's neutral. Unlike Ruby, Python, JavaScript, or PHP, Bash offends equally across all communities. ;) 7 | * It's made to be glue. Write complex parts in C or Go (or whatever!), and glue them together with Bash. 8 | 9 | This document is how I write Bash and how I'd like collaborators to write Bash with me in my open source projects. It's based on a lot of experience and time collecting best practices. Most of them come from these [two](http://wiki.bash-hackers.org/scripting/obsolete) [articles](http://www.kfirlavi.com/blog/2012/11/14/defensive-bash-programming/), but here integrated, slightly modified, and focusing on the most bang for buck items. Plus some new stuff! 10 | 11 | Keep in mind this is not for general shell scripting, these are rules specifically for Bash and can take advantage of assumptions around Bash as the interpreter. 12 | 13 | ## Big Rules 14 | 15 | * Always double quote variables, including subshells. No naked `$` signs 16 | * This rule gets you pretty far. Read http://mywiki.wooledge.org/Quotes for details 17 | * All code goes in a function. Even if it's one function, `main`. 18 | * Unless a library script, you can do global script settings and call `main`. That's it. 19 | * Avoid global variables. Though when defining constants use `readonly` 20 | * Always have a `main` function for runnable scripts, called with `main` or `main "$@"` 21 | * If script is also usable as library, call it using `[[ "$0" == "$BASH_SOURCE" ]] && main "$@"` 22 | * Always use `local` when setting variables, unless there is reason to use `declare` 23 | * Exception being rare cases when you are intentionally setting a variable in an outer scope. 24 | * Variable names should be lowercase unless exported to environment. 25 | * Always use `set -eo pipefail`. Fail fast and be aware of exit codes. 26 | * Use `|| true` on programs that you intentionally let exit non-zero. 27 | * Never use deprecated style. Most notably: 28 | * Define functions as `myfunc() { ... }`, not `function myfunc { ... }` 29 | * Always use `[[` instead of `[` or `test` 30 | * Never use backticks, use `$( ... )` 31 | * See http://wiki.bash-hackers.org/scripting/obsolete for more 32 | * Prefer absolute paths (leverage $PWD), always qualify relative paths with `./`. 33 | * Always use `declare` and name variable arguments at the top of functions that are more than 2-lines 34 | * Example: `declare arg1="$1" arg2="$2"` 35 | * The exception is when defining variadic functions. See below. 36 | * Use `mktemp` for temporary files, always cleanup with a `trap`. 37 | * Warnings and errors should go to STDERR, anything parsable should go to STDOUT. 38 | * Try to localize `shopt` usage and disable option when finished. 39 | 40 | If you know what you're doing, you can bend or break some of these rules, but generally they will be right and be extremely helpful. 41 | 42 | ## Best Practices and Tips 43 | 44 | * Use Bash variable substitution if possible before awk/sed. 45 | * Generally use double quotes unless it makes more sense to use single quotes. 46 | * For simple conditionals, try using `&&` and `||`. 47 | * Don't be afraid of `printf`, it's more powerful than `echo`. 48 | * Put `then`, `do`, etc on same line, not newline. 49 | * Skip `[[ ... ]]` in your if-expression if you can test for exit code instead. 50 | * Use `.sh` or `.bash` extension if file is meant to be included/sourced. Never on executable script. 51 | * Put complex one-liners of `sed`, `perl`, etc in a standalone function with a descriptive name. 52 | * Good idea to include `[[ "$TRACE" ]] && set -x` 53 | * Design for simplicity and obvious usage. 54 | * Avoid option flags and parsing, try optional environment variables instead. 55 | * Use subcommands for necessary different "modes". 56 | * In large systems or for any CLI commands, add a description to functions. 57 | * Use `declare desc="description"` at the top of functions, even above argument declaration. 58 | * This can be queried/extracted using reflection. For example: 59 | ``` 60 | eval $(type FUNCTION_NAME | grep 'declare desc=') && echo "$desc" 61 | ``` 62 | * Be conscious of the need for portability. Bash to run in a container can make more assumptions than Bash made to run on multiple platforms. 63 | * When expecting or exporting environment, consider namespacing variables when subshells may be involved. 64 | * Use hard tabs. Heredocs ignore leading tabs, allowing better indentation. 65 | 66 | ## Good References and Help 67 | 68 | * http://wiki.bash-hackers.org/scripting/start 69 | * Especially http://wiki.bash-hackers.org/scripting/newbie_traps 70 | * http://tldp.org/LDP/abs/html/ 71 | * Tips for interactive Bash: http://samrowe.com/wordpress/advancing-in-the-bash-shell/ 72 | * For reference, [Google's Bash styleguide](https://google.github.io/styleguide/shell.xml) 73 | * For linting, [shellcheck](https://github.com/koalaman/shellcheck) 74 | 75 | ## Examples 76 | 77 | ### Regular function with named arguments 78 | Defining functions with arguments 79 | ```bash 80 | regular_func() { 81 | declare arg1="$1" arg2="$2" arg3="$3" 82 | 83 | # ... 84 | } 85 | ``` 86 | 87 | ### Variadic functions 88 | Defining functions with a final variadic argument 89 | ```bash 90 | variadic_func() { 91 | local arg1="$1"; shift 92 | local arg2="$1"; shift 93 | local rest="$@" 94 | 95 | # ... 96 | } 97 | ``` 98 | 99 | ### Conditionals: Testing for exit code vs output 100 | 101 | ```bash 102 | # Test for exit code (-q mutes output) 103 | if grep -q 'foo' somefile; then 104 | ... 105 | fi 106 | 107 | # Test for output (-m1 limits to one result) 108 | if [[ "$(grep -m1 'foo' somefile)" ]]; then 109 | ... 110 | fi 111 | ``` 112 | 113 | ### More todo 114 | --------------------------------------------------------------------------------