How can I safely get the version of ksh?

How can I safely get the version of ksh from within a ksh script?

I have seen the following solutions:

  1. ksh --version
  2. echo ${.sh.version}
  3. echo $KSH_VERSION

And given the right circumstances, each of these works correctly. However, I care about the non-perfect case.

Specifically, there are several machines I work with that have older versions of ksh that, for my purposes, are severely lacking in functionality. Anyway, the reason I want to check the version (programmatically) is to see if the ksh version is one of the less capable versions; and if so, I want to execute a branch with less awesome code.

However, on the problematic machines, the shell’s ineptitude extends into checking the version…

  • If I try ksh --version, it prints nothing and opens a new instance of ksh!
  • If I try echo ${.sh.version}, ksh treats this as a syntax error that cannot be discarded with 2> /dev/null.
    $ echo ${.sh.version} 2> /dev/null  
    ksh: ${.sh.version}: bad substitution
  • Of course echo $KSH_VERSION appears to work fine – I mean it won’t crash – though on these machines it’s blank. Also, I saw somewhere that KSH_VERSION is set only by pdksh.

Questions:

  • How can I safely check the version of ksh programmatically? For my purposes here, I don’t really care what the actual version number is, just whether it’s an outdated version of ksh.
  • Is $KSH_VERSION good enough? I mean if it’s blank, then is ksh necessarily an outdated version? Was that other forum correct that it might not be set even for newer versions of ksh?
  • Is there just no way to check this at all?

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

KSH_VERSION was not implemented in ksh93 before version 93t. It will be set in mksh, pdksh, lksh. So for checking the version of ksh, we can try these steps:

  • Checking KSH_VERSION to detect mksh, pdksh, lksh
  • If first step fails, try a feature that’s different between ksh93 and ksh88/86 (Let David Korn show us).

With these in mind, I will go with:

case "$KSH_VERSION" in
  (*MIRBSD*|*PD*|*LEGACY*) printf '%sn' "$KSH_VERSION" ;;
  (*) [ -z "$ERRNO" ] && printf '%sn' "${.sh.version}" || echo ksh88/86 ;;
esac

Method 2

I think that .sh.version has existed ever since the first version of ATT ksh 93. It isn’t available in pdksh or mksh. Since ${.sh.version} is a syntax error in shells other than ksh93, wrap the test for it in a subshell and protect it behind eval.

_sh_version=$(eval 'echo "${.sh.version}"' 2>/dev/null) 2>/dev/null
case $_sh_version in
  '') echo "This isn't ATT ksh93";;
  …
esac

KSH_VERSION started out in the public domain ksh clone (pdksh), and was added to the actual Korn shell relatively recently, in 2008 with ksh93t.

Rather than test for a version number, you should test for the specific feature that’s giving you grief. Most features can be tested for by trying some construct in a subshell and see if it triggers an error.

Method 3

For “real” ksh releases (i.e. AT&T based), I use this command:

strings /bin/ksh | grep Version | tail -2

Here are various output I get:

Original ksh:

@(#)Version M-11/16/88i

dtksh;

@(#)Version 12/28/93
Version not defined

Modern ksh93:

@(#)$Id: Version AJM 93u+ 2012-08-01 $

For pdksh/msh ksh clones and modern AT&T ksh versions too, here is something that works:

$ mksh -c 'echo $KSH_VERSION'
@(#)MIRBSD KSH R50 2015/04/19

Edit:

I overlooked you were asking about doing it from inside a script, not by knowing the path to the tested ksh binary.

Assuming you really want the version of ksh used, and not the features it supports, here is one way to do it using only the strings command that should work on at least on Linux and Solaris:

echo $(for i in $(find /proc/$$ ! -type d ! -name "pagemap" | 
  grep -v "/path/" | grep -v "/fd/" ) ; do
  strings $i | egrep "([V]ersion|[K]SH_VERSION).*[0-9]" | sort -u
done 2>/dev/null)

Note that this method is unreliable as /proc might not be mounted, and there are certainly other weaknesses. It is untested on other Unix OSes.

Method 4

While I was writing a script for ksh, I noticed that the -a option of ksh’s built-in whence command appears to not be supported in older versions of ksh. And this appears to be true on all the systems I checked, which included Solaris, AIX, HP-UX, and Linux.

So here is the solution as a ksh function:

is_modern_ksh() {
  if whence -a whence > /dev/null 2>&1 ; then
    return 0 #success -> true
  fi
  #Else the call to `whence` failed because `-a` is not supported
  return 1 #failure -> false
}

And here’s how to use it:

if is_modern_ksh ; then
  echo "You're using a MODERN version of ksh. :)"
else
  echo "You're using an OLD version of ksh. :("
fi

Method 5

CTRL+ALT+V

or

ESC , CTRL+V

Have typically proven very reliable as far as interactively determining the version of KSH you’re using, however scripting them has proven more difficult.

Method 6

I think the fundamental problem with using ${.sh.version} is that ksh88 just stops, with a non-zero exit code.

So my solution is to put the code that references ${.sh.version} in a sub-shell, then test to see if the sub-shell exits non-zero and have code in the sub-shell that will work on versions of the ksh where referencing ${.sh.version} does work. Wrapping it into a function that is then called by another function that reverses the return code, so that the final call is checking for true.

function is_oldksh
{
    (test -n ${.sh.version}) 2>/dev/null
}

function oldkshtest
{

    is_oldksh || return 0 && return 1
}

oldkshtest && echo "old ksh" || echo "new ksh"

I have run this on AIX and Oracle Enterprise Linux 5 & 6, with ksh88, ksh93 and pdksh.

Pete

Method 7

The following seems to work reasonably well for all the shells I’ve tested, including old ksh88e and a nearly complete range of common Ksh clones (though only one version of each), though I’ve not yet tested an actual original Bourne shell (and doing so may require adapting the test expression for older versions….

Addendum:

I’ve now also successfully tested this with Heirloom Bourne Shell, although with an external (and more modern) test program.

is_attksh()
{
    # ksh93
    _sh_version=$(eval 'echo "${.sh.version}"' 2>/dev/null)
    # pdksh only
    _opt_login=$(set -o | grep login)

    test -n "${_sh_version}" -o ( -z "${_opt_login}" -a -n "${_}" -a -n "${ERRNO}" -a -n "${FCEDIT}" -a -n "${PS3}" )
}
is_attksh && echo "AT&T Ksh${_sh_version:+: }${_sh_version:- (probably ksh88 or ksh86)}" || echo "not real ksh"

is_zsh()
{
    test -n "${ZSH_VERSION}"
}
is_zsh && echo "Zsh: ${ZSH_VERSION}" || echo "not zsh"


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x