views:

52

answers:

2

The main question here is: is there a standard method of writing UNIX shell scripts that will run on multiple UNIX platforms.

For example, we have many hosts running different flavours of UNIX (Solaris, Linux) and at different versions all with slightly different file system layouts. Some hosts have whoami in /usr/local/gnu/bin/, and some in /usr/bin/.

All of our scripts seem to deal with this in a slightly different way. Some have case statements on the architecture:

case "`/script/that/determines/arch`" in 
  sunos-*) WHOAMI=`/usr/local/gnu/bin/whoami` ;;
  *)       WHOAMI=`/usr/bin/whoami` ;;
esac

With this approach you know exactly what binary is being executed, but it's pretty cumbersome if there are lots of commands being executed.

Some just set the PATH (based on the arch script above) and call commands by just their name. This is convenient, but you lose control over which command you run, e.g. if you have:

/bin/foo
/bin/bar
/other/bin/foo
/other/bin/bar

You wouldn't be able to use both /bin/foo and /other/bin/bar.

Another approach I could think of would to have a local directory on each host with symlinks to each binary that would be needed on each host. E.g.:

Solaris host:

/local-bin/whoami -> /usr/local/gnu/bin/whoami
/local-bin/ps -> /usr/ucb/ps

Linux host:

/local-bin/whoami -> /usr/bin/whoami
/local-bin/ps -> /usr/ps

What other approaches do people use? Please don't just say write the script in Python... there are some tasks where bash is the most succinct and practical means of getting a simple task accomplished.

+3  A: 

I delegate all this to my .profile, which has an elaborate series of internal functions to try likely directories to add to the PATH. Except on OSX, which I believe is basically impossible because Darwin/Fink/Ports each wants to control your PATH, this approach works well enough.

If I cared about ambiguity (multiple instances of foo in different directories on my PATH), I would modify the functions so as to identify all ambiguous commands and require manual resolution. But for my environment, this has never been an issue. My main concern has been to have a single .profile that runs on Debian, Red Hat, Solaris, BSD, and so on. The 'try every directory that could possibly work' approach works well enough.

Norman Ramsey
It's not ideal, but I guess it's probably the better of all the evils.
SimonC
Just to clarify, we run a lot of commands with sudo, and in our environment, that does not cause the impersonated user's .profile to be read in. I'm not sure if that's just how sudo works or if we've not got something set up correctly though.
SimonC
A: 

To set PATH to POSIX-compliant directories you can do the following at the beginning of your Bash scripts:

unset PATH
PATH="$(PATH=/bin:/usr/bin getconf PATH)"
export PATH

If you know you can use Bash across different Unix systems, you may use shell builtins instead of external commands to improve portability as well. Example:

help type
type -a type

type -P ls  # replaces: which ls

To disable alias / function lookup for commands such as find, ls, ... in Bash you may use the command builtin. Example:

help command

command ls -l

If you want to be 100% sure to execute a specific command located in a specific directory, using the full executable path seems the way to go. First match wins in PATH lookup!

yabt