[FFmpeg-devel] portable shell programming
Diego Biurrun
diego
Sun Sep 27 16:06:34 CEST 2009
Since the topic cropped up after Stefano committed some bashisms, I
thought this might be helpful.
I recently wrote a quick guide to portable shell programming at work,
I'm pasting it here. It's neither complete nor exhaustive, but it's a
start and it should help avoid the most glaring mistakes. Further
comments and additions are welcome.
But first maybe I should explain what the point of this whole exercise
is. Why bother fixing bashism when bash is ubiquitous free software?
It's true that bash is available everywhere and yes, it is the default
shell on most modern systems. Or rather, it is the default
*interactive* shell on most modern systems.
bash has some drawbacks. It is
- slow,
- bloated,
- has nonstandard syntax extensions.
Nowadays this tends to make people yawn and say that they don't care if
program XYZ uses up another 0.1% of their CPU cycles and/or another 0.1%
of their memory.
However, FFmpeg is expected to be portable and work on small embedded
systems.
Even for fullblown desktop machines performance is important. Ubuntu
switched from bash to dash as /bin/sh (not default interactive shell)
and boot speed improved by around 30% IIRC.
Furthermore, nobody likes it when Microsoft bends and extends standards,
so you should not let the GNU people get away with it.
Notice that both Ubuntu and Debian use dash instead of bash nowadays.
I'm not sure about other environments, but there are more and the number
is increasing.
OK, now that you are convinced that writing portable shell scripts is
the way forward, here is how you go about it:
* default shell
Make sure your system does not use bash as /bin/sh.
* correct shebang line
Add a proper '#!/bin/sh' at the top of your shell scripts. Don't use
/bin/bash there. Don't leave out the shebang line, behavior is
non-deterministic without it.
* 'source' vs. '.'
Use '.' instead of 'source' when including other files in your shell
script. 'source' is bash-specific.
* 'let' and '(( ))'
Both are bashisms. Use arithmetic expansion with '$(( ))' instead.
* skip the 'function' keyword for function declarations
Just use
foo() { ....}
instead of
function foo() { ....}
Bash understands both, POSIX only specifies the former, so use that.
* '==' in test invocations
The builtin 'test' command in bash understands '==', but POSIX test
does not and the builtin in other shells may or may not understand
'=='. Just use '=' instead.
* 'echo' with backslash escapes
The builtin 'echo' in some shells does not support backslash escapes.
Use '/bin/echo' or 'printf' instead if you absolutely need escape
sequences.
* 'read' only works with a variable
Write 'read line' instead of 'read', it's that simple.
* 'getopt' vs. 'getopts'
The one with the 's' at the end is bash-specific, prefer the other.
* some parameter expansion expressions are non-portable
An example is '${CMD:1}'. There are more bash-specific ones that I
don't remember offhand. Use just the POSIX-compatible ones. See
http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_02
* `` vs. $()
Both are POSIX-compatible and backticks are even a tad more portable,
but I wholeheartedly recommend the $() syntax. Nesting backticks is
possible, but will drive you crazy in a very short amount of time.
If you want to know if any bashisms crept into your script, try
'checkbashisms' from the Debian devscripts package. It will check
your scripts for the most common bashisms. But the most important
part is checking your scripts with dash or another POSIX-compatible
non-bash shell.
The POSIX specification is hosted at
http://www.opengroup.org/onlinepubs/009695399/
Refer to the "Shell & Utilities" section, also at
http://www.opengroup.org/onlinepubs/009695399/idx/xcu.html
That's it for now. Further questions and feedback are welcome.
Diego
More information about the ffmpeg-devel
mailing list