Defensive bash programming is good and important, but this article will not help you do it. Rather, this article is full of advice that is so bad that I suspect that it was written as a satire about bad programming advice.
The very first line of code in the article has a bug due to not being sufficiently defensive:
readonly PROGNAME=$(basename $0)
Oh yeah, that’s gonna work real fucking well when $0 is “/home/kragen/buggy software/kfirlavi/rebuild”. This kind of stupid bug is everywhere in this article.
local files=$@
local i
for i in $files
Great, let’s call our filename “i” so that people will think it’s an integer once the loop is more than ten lines long. And hope that they don’t confuse “$i” with “$1”. And also introduce a completely unnecessary bug in handling filenames with spaces; for filename in "$@" would have been clearer, shorter, and most importantly, correct.
Also, as apenwarr writes, “Assigning $@ to a variable is unspecified and it doesn’t do anything consistent.”
main()
Intuitive for functional programming
This shows that the author doesn’t know what “functional programming” means. Or, arguably, “intuitive”.
is_not_empty() {
local var=$1
[[ -n $var ]]
}
Great, so if is_not_empty errors.log; then echo see errors.log >&2; fi will always print an error message! Whereas if you use [[ -n errors.log ]] then the bug is obvious if you know ksh or bash already.
Here’s what I recommend for defensive bash:
never let bash autosplit your variable expansions; use doublequotes for that.
If you need aggregate data structures, consider using argument lists, multi-line files (possibly via a pipe), or using a different programming language, or using bash arrays. Not variables containing space-separated words.
always -print0 and -0 for find and xargs. Newlines in filenames are uncommon, but xargs splits on spaces too by default!
set -e at the beginning, always. You can use ||: if need be to ignore failures silently. (apenwarr disagrees.)
use trap 0 for cleanup.
don’t use /tmp unless you’re on a Mac. You’re creating open season for tempfile races.
use ${1:?errmsg} to ensure you’re getting called with enough parameters.
Assuming it’s not actually a satire, @kfirlavi should be commended for sharing his ideas, which is an important part of the process of learning, but he doesn’t seem to realize that he’s still at a very basic level of competence with bash scripting (witness his comments looking down on bash scripts that are even worse than his), and as a result his advice is not very good.
I agree with your opinion but I think what the author of the article is trying to point out is: “How to use Bash securely” not “What to use instead of Bash”.
I can’t take any article on bash programming seriously when it uses unquoted variables, especially one about defensive coding.
Defensive bash programming is good and important, but this article will not help you do it. Rather, this article is full of advice that is so bad that I suspect that it was written as a satire about bad programming advice.
The very first line of code in the article has a bug due to not being sufficiently defensive:
Oh yeah, that’s gonna work real fucking well when $0 is “/home/kragen/buggy software/kfirlavi/rebuild”. This kind of stupid bug is everywhere in this article.
Great, let’s call our filename “i” so that people will think it’s an integer once the loop is more than ten lines long. And hope that they don’t confuse “$i” with “$1”. And also introduce a completely unnecessary bug in handling filenames with spaces;
for filename in "$@"would have been clearer, shorter, and most importantly, correct.Also, as apenwarr writes, “Assigning $@ to a variable is unspecified and it doesn’t do anything consistent.”
This shows that the author doesn’t know what “functional programming” means. Or, arguably, “intuitive”.
Great, so
if is_not_empty errors.log; then echo see errors.log >&2; fiwill always print an error message! Whereas if you use[[ -n errors.log ]]then the bug is obvious if you know ksh or bash already.Here’s what I recommend for defensive bash:
-print0and-0for find and xargs. Newlines in filenames are uncommon, but xargs splits on spaces too by default!set -eat the beginning, always. You can use||:if need be to ignore failures silently. (apenwarr disagrees.)trap 0for cleanup./tmpunless you’re on a Mac. You’re creating open season for tempfile races.${1:?errmsg}to ensure you’re getting called with enough parameters.Also, see Insufficiently known POSIX shell features, linked in the comment thread.
Assuming it’s not actually a satire, @kfirlavi should be commended for sharing his ideas, which is an important part of the process of learning, but he doesn’t seem to realize that he’s still at a very basic level of competence with bash scripting (witness his comments looking down on bash scripts that are even worse than his), and as a result his advice is not very good.
[Comment removed by author]
I agree with your opinion but I think what the author of the article is trying to point out is: “How to use Bash securely” not “What to use instead of Bash”.
[Comment removed by author]
It’s not good advice. It’s terrible advice. See my longer top-level comment for details.
Do you recommend using Bash at all given how hard it is to use correctly?
Yes, because there isn’t a plausible alternative. I mean Plan9
rcis less bug-prone but where do you have it installed?