Defensive BASH programming(kfirlavi.com) |
Defensive BASH programming(kfirlavi.com) |
Three real defensive bash programming tips are:
- Quote all uses of variables
- set -o nounset
- set -o errexit
And many others can be found in and around http://mywiki.wooledge.org/BashFAQ
Using pipefail, if any program in a pipeline fails (i.e. exit code != 0), then the exit code for the pipeline will be != 0.
E.g. pipefail can be useful to ensure `curl does-not-exist-aaaaaaa.com | wc -c` doesn't exit with exit code 0..!
set -o nounset -o pipefail -o errexit #!/...
set -eu
# ... Main part of your script ...The latter makes it clearer exactly what features are being enabled, and it's a bit of a false economy to try and "shorten" the script like this.
I agree that the function call introduces a better name. The problem is that this name is specific to the author of the script. It replaces a reusable tricky knowledge of the language by a simple knowledge of the author usages.
If we take into account the increased verbosity, increased typing, increased number of lines, there is a clear loss in using these functions.
For me, adding a comment would be far enough and far less annoying.
It'd be like defining an "and" function and writing
if ( and(conditionA, conditionB) ) {
//do something
}
because "&&" is too confusing. It's just syntax. Learn it.And on the subject of &&, it's interesting that in his example about clarity he chooses to use short-circuit and for brevity instead of the more readable if block.
main() {
local files=$(ls /tmp | grep pid | grep -v daemon)
}The text read:
- Second example is much better. Finding files is the problem of temporary_files() and not of main()’s. This code is also testable, by unit testing of temporary_files().
- If you try to test the first example, you will mish mash finding temporary files with main algorithm.
That the author is using that to find files is reasonable enough to me to disregard the rest of the blog.
edit: take that back...looked through rest of blog but don't see anything useful. I hate his idea for functions for builtins, using local is ok, please don't break up shell pipelines over N lines for simple stuff
I used to write my various glue-things-together scripts in bash, but this quickly becomes a nightmare as the script grows, due to bash's corner cases, syntax, portability issues etc.
Recently I wrote my massive glue-things-together script with nodejs (since I already use node for many things) and it's much more maintainable and I couldn't be more happy. Node 0.12 has execSync which was the missing piece for making node the proper shell scripting platform.
If you are interested, you may want to check shelljs [1] and my snippets for robust `exec()` and `cd()` in node.
[1] https://github.com/shelljs/shelljs [2] https://gist.github.com/jakub-g/a128174fc135eb773631
sudo -u 2>&1 >> logfile | tee | echoAt any rate, you'd:
1. use some sort of popen()
2. use pipes and pass the same pipe to stdout as stderr
3. pass stdout of one popen to another's stdin
4. just use bash, because this gets so hairy and bash does it really well. When I want to use a complex pipeline of programs in non-bash programs, I'll frequently popen() to a bash script. This is ugly, but if you're really careful (shellshock?) it can be ok.
Another very good resource is the O'Reilly book: http://shop.oreilly.com/product/9780596009656.do
Any sufficiently-complex shell script can usually be written clearly as a Python or Perl program for instance, without having to worry about how the code might be misinterpreted.
Yes, I write shell scripts sometimes. I just make sure they're doing something pretty straightforward.
`find -print0 | xargs -0 grep some_pattern'
find ... -print0 | xargs -0 ... [eventually with -n1] ls $dir \
| grep something
is the same as ls $dir |
grep somethingI've been enjoying BATS [0] for my bash testing.
Whether doing so or not is a good idea is another question entirely..
set -u
set -e
with trap 'echo $0 internal error at $LINENO' ERRhttps://google.github.io/styleguide/shell.xml?showone=Use_Lo...
set -eu -o pipefailBy the way, there are systems where 'print0' is a recognized parameter for the default find, but not '+'. RHEL 4.x comes to mind.
But if I understand you correctly on RHEL 4.X `print0` works but `-exec cmd {} +` doesn't.
Which is to say I disagree with op that it's better to rely on `exec cmd {} +` when it seems you're more likely to have `print0 and xargs -0` then that.