Many perl people like to go on and on about Bourne shell not being portable. However, for some reason, you don't really encounter that in the python world. Also, Bourne shell scripts actually are more easy to write portably than C programs are... Anyway, here's a list of things to do and/or not do, to come up with a portable Bourne shell script, followed by a list of features that do and do not work in various OS's Bourne shells.
For Bourne shell portability: 1) Use [ a = b ] && [ c = d ] instead of [ a = b -a c = d ], and [ a = b ] || [ c = d] instead of [ a = b -o c = d]. -a and -o give inconsistent operator precedence across platforms. 2) Use echo foo | tr -d '\012' instead of echo -n foo or echo 'foo\c' to suppress newline. 3) Use a big case statement on $1 and recursively call your script with "$0" 'behavior', instead of using shell functions (but shell functions are very common now anyway - the only /bin/sh I ever used that didn't have them was on Ultrix, which is long gone now). You can also do this with symlinks to your program, and case'ing on $0. 4) If you set a variable inside of a loop, don't assume that it'll have the same value when the loop is exited; Some /bin/sh's implement loops by spawning subshells. It's better to write the value to a temporary file, and read it into a variable after the loop is exited. Or alternatively, echo your value to stdout, and then capture the result with back quotes. Yes, you can put back quotes around an entire series of nested for loops or whatever. 5) Use awk to separate a variable on a delimiter instead of cut. Some systems have awk but not cut.
Specific features supported and not supported: Shell functions work: OSF1-4.0F no SunOS-5.7 no RHEL-3 yes IRIX-6.5.20m yes AIX-5.1-ML4 yes Setting a variable within a loop and using the variable after the loop ends works: OSF1-4.0F yes SunOS-5.7 yes RHEL-3 yes IRIX-6.5.20m yes AIX-5.1-ML4 yes