4

I have two servers one AIX with default shell ksh, the other RHEL with default shell bash.

I have a script that is mounted on both that will run similar commands but for either AIX or Linux. This script does not work on the bash servers, is there a way to make this script run on both bash and ksh, or would the best option be to create two different scripts?

#!/usr/bin/ksh
export OS=`uname -s`
echo "OS is "$OS"."
case $OS in
        "AIX")
                #run AIX commands;;
        "Linux")
                #run Linux commands;;
        "*")
                echo "Exiting. The OS type is not found.";;
esac
echo "Done."
exit 0

UPDATE

The commands I need to run are for user accounts on each server. An example of unlocking an account. AIX /usr/bin/chuser account_locked=false $USERNAME

Linux /usr/bin/passwd -u $USERNAME

Through further investigation I have found that the the shells location in AIX are located at /usr/bin/sh, while Redhat's are located at /bin/sh.

Can I define the shebang based on the results of "uname"?

2
  • If AIX commands and Linux commands are very similar, it may make sense to put them in the same script with some extra code to handle the differences. If AIX commands and Linux commands are completely different, it may make more sense to have two different versions of the script.
    – jw013
    Aug 5, 2014 at 20:16
  • 2
    I’m curious whether the issue is (1) the difference between AIX commands and Linux commands, or (2) the difference between ksh scripting style and bash scripting style. In case 1, I would agree with @jw013 and encourage you to assess how much commonality there would be the two scripts, and how much specialized stuff, and decide based on that. But if it’s 2, I would suggest that you back out the ksh-isms and “dumb down” the script to follow POSIX shell conventions, so it works in both shells. Aug 5, 2014 at 22:08

5 Answers 5

3

The easiest way by far is to use the same shell on both systems. Just because one shell is preinstalled (there's no such thing as a “default shell” for scripts, the shell is whatever the shebang line says) doesn't mean that you can't install others. You can install bash on AIX (from the toolbox, for example), or ksh93 for Linux (with your distribution's package manager: install the ksh package).

If you pick ksh, beware that /usr/bin/ksh on AIX is ksh88. There is no version of ksh88 for Linux, only ksh93, which is available as /usr/bin/ksh93 on AIX.

It is easier if the same shell is installed at the same location on both systems (a symbolic link is fine), so that you can use the same path in the shebang line. If having the same location is too difficult, you can use something like #!/usr/bin/env bash, as long as you ensure that bash is in the PATH on both systems. This may be useful if you have to install bash or ksh in your home directory because you don't have root access.

If you really can't install the same shell on both machines — presumably because there's some silly compliance rule that only complicates people's life but is too entrenched to overturn — you have a few possibilities.

  • Use #!/bin/sh as your shebang line and program in the intersection of what your systems offer. All modern unix systems provide a POSIX shell as /bin/sh. On AIX, /bin/sh is ksh88. On Red Hat, /bin/sh is bash (which behaves slightly differently when invoked as sh). Beware that on some other operating systems (for example, on many Linux distributions), /bin/sh is a smaller shell which may not have much more than POSIX features.
  • Start your scripts with some boilerplate code that looks for a better shell and executes it.

    #!/bin/sh
    if [ -n "$BASH" ]; then
      … bash compatibility code …
    elif type whence >/dev/null 2>/dev/null; then
      … ksh compatibility code …
    elif type ksh93 >/dev/null 2>/dev/null; then
      exec ksh93 "$0" "$@"
    elif type ksh >/dev/null 2>/dev/null; then
      exec ksh "$0" "$@"
    elif type mksh >/dev/null 2>/dev/null; then
      exec mksh "$0" "$@"
    elif type bash >/dev/null 2>/dev/null; then
      exec bash "$0" "$@"
    else
      echo 1>&2 "Cannot find ksh or bash, aborting"
      exit 125
    fi
    

In either case, the intersection of ksh88 and bash provides some useful features beyond POSIX, but you may need a little compatibility code. In particular:

  • Array assignment in ksh88 uses set -A. See assignation variable under different ksh environment
  • Local variables are declared by typeset.
  • Ksh88 doesn't have ${VAR/PATTERN/REPLACEMENT}, $'…' or FIGNORE. It does have [[ … ]].
  • To enable @(…) and other ksh extended patterns in bash, run shopt -s extglob.
2

It would probably be easier to have two separate versions of the script. Since it is short it may not be worth adding extra code to handle the differences between the two formats as jw013 suggested. On the other hand if you had a larger script it would probably be easier to have one and make the script execute different commands depending on where it is running from.

1

I had a similar requirement - I needed to use ksh93 on AIX 5.3 to support associative arrays. So I set up the script to start a second instance using ksh93. I also added a safeguard to keep it from respawning itself over and over.

#!/bin/ksh

scr=$0
safe=$1
echo "Check OS to determine if ksh93 is needed..."
if ( `typeset -A testvar > /dev/null 2>&1` ); then
  echo "Associative array functions supported."
else
  echo "Associative array functions NOT supported. Starting second instance with ksh93..."
  if [ "$safe" -eq 1 ]; then
    echo "SECOND INSTANCE ALREADY STARTED!"
    exit 1
  else
    echo "BEGIN SECOND INSTANCE USING KSH93"
    /bin/ksh93 $scr 1
    exit 0
  fi
fi
0

I would put some work into seeing if you can actually have one version with command that work on both systems.

I had this issue with my .bashrc file which I keep in my dotfiles repository. I want it to work on both OSX and Ubuntu and there were a few command I had to modify to do that. In this case it was mostly which switches worked for certain command or how a test command was written. I tackled each issue, finding a way to do whatever command I was trying in a form that would work on both OS's

I tried the 'two different files' and 'two different sections depending on OS' approaches and found them a pain to maintain.

0

Gilles's answer is (IMHO) brilliant (as always), but since you didn't choose it or even up-vote it, perhaps you find it too complicated. So here are a few (simple) points that might or might not be helpful, and may help you avoid maintaining two separate scripts, which could turn into madness for a large script that has to be maintained over a long period of time.

1) The AIX machines might have bash installed even though it's not the default shell for interactive use. (The AIX machines I work with do have bash installed, but I don't know if it came that way of the sysadmins added it. I'm guessing it came on it, since all our sysadmins are ksh junkies.) And I'm willing to bet that the RHEL machines have ksh installed. Check this, because if you can use the same shell on both machines it will save you a lot of hassle.

2) If you don't know where the shell is going to be installed, avoid putting its path directly in the shebang line. Instead put in the path to env and the shell executable name afterward, like so:

#!/usr/bin/env bash

This should work on both machines, but you'll have to test it to be sure. (The location for env is supposedly more standard than the location for bash or most other things.)

Once you're using the same shell on both machines, the problem might be solved right there. But if not...

3) You can break your platform-specific code out into functions that you source into the main script. This means that although script might have lots of places where things work differently depending on whether it's running on AIX or RHEL, you don't need to repeatedly check the platform. Just do it once, and source the appropriate functions:

case $OS in
    "AIX")
            source aix_functions;;
    "Linux")
            source redhat_functions;;
    "*")
            echo "Exiting. The OS type is not found.";;
esac

Now you can use whatever functions you sourced, all over your script without ever having to have another case statement to distinguish between platforms:

do_something_platform_specific "$SOME_ARGUMENT" "$SOME_OTHER_ARGUMENT"

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .