Bash scripting has evolved a lot in the last ten years.
I get used to relay on bash for all normal “data domination” tasks (like file system refactorings, database extraction and reporting and so on) and switching to python/ruby/perl when the complexity gets bigger. I didn’t imagine bash is even more powerful :) This article by Robert Muth so you new nice tricks: I add here some other like mapfile and stress the most important one.
Fail early
First of all, start every script with
[bash]#!/bin/bash
set -o nounset
set -o errexit
[/bash]
So your script will alt if an error occur.
The magic $@ is beter then $*
The $@ is to be preferred instead of $* when you must pass parameters to other scripts because it sees arguments as separate word. I want to know it ten years ago!
Using $@ you can do wrapper script which can pass around input to other stuff.
No more temp files with <()
The <() operator is very nice to provide the output of a command like a file, so you avoid creating temp file names (no more $$ magic!)
[bash]# download and diff two webpages
diff <(wget -O – url1) <(wget -O – url2)
[/bash]
Reporting needs?
A nice feature is mapfile because it read lines from the standard input into an indexed array variable. You can also avoid the loop:
[bash]
mapfile -t filecontent <"$logfile"
#….
printf "%s\n" "${filecontent[@]}"
[/bash]
It also support reading from a file descriptor and also a callback function too.
Piping inside mapfile seems not to work in some case (I think by bash is too old and buggy), so the following way
[bash]
< <(find . ) mapfile -t -n 3 ;echo "${MAPFILE[@]}"
mapfile -t < <(printf ‘%s\n’ {1..5000} )
[/bash]
is the suggested one by me: it also helps to remember the useful <() operator.
As you see is also possible to use prinf to make loop ranges (line 2).
Collapsing Functions
A collapsing function is a function whose behavior changes depending upon the circumstances under which it’s run. Function collapsing is useful when you find yourself repeatedly checking a variable whose value never change.
[bash]
#!/bin/bash
[[ $1 = -v || $1 = –verbose ]] && verbose=1
chatter() {
if [[ $verbose ]]; then
chatter() {
echo "$@"
}
chatter "$@"
else
chatter() {
:
}
fi
}
[/bash]
The trick is is to redefine the funtcion upon first run.
If not abused, the trick is quite clean to use.
Last but not least: blogging!
Blogging rocks, you know ya?. So this wonderful bashblog script at https://github.com/cfenollosa/bashblog will give you a sneak peek on how to write a well structured application…in bash!