How can I safely get the version of ksh?
Solution 1
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
Solution 2
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 detectmksh
,pdksh
,lksh
- If first step fails, try a feature that's different between
ksh93
andksh88/86
(Let David Korn show us).
With these in mind, I will go with:
case "$KSH_VERSION" in
(*MIRBSD*|*PD*|*LEGACY*) printf '%s\n' "$KSH_VERSION" ;;
(*) [ -z "$ERRNO" ] && printf '%s\n' "${.sh.version}" || echo ksh88/86 ;;
esac
Solution 3
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.
Solution 4
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.
Solution 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.
Related videos on Youtube
Comments
-
Sildoreth over 1 year
How can I safely get the version of ksh from within a ksh script?
I have seen the following solutions:
ksh --version
echo ${.sh.version}
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 ofksh
! -
If I try
echo ${.sh.version}
,ksh
treats this as a syntax error that cannot be discarded with2> /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 thatKSH_VERSION
is set only bypdksh
.
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 ofksh
. - Is
$KSH_VERSION
good enough? I mean if it's blank, then isksh
necessarily an outdated version? Was that other forum correct that it might not be set even for newer versions ofksh
? - Is there just no way to check this at all?
-
Thorbjørn Ravn Andersen almost 9 yearsAny reason you want two code paths and not just a single one with less awesome code?
-
Sildoreth almost 9 years@ThorbjørnRavnAndersen it has to do with the prompt. In my .kshrc file, I have a function that simulates the pwd-abbreviating functionality of tcsh and zsh prompts, and I set up
PS1
to use this function. However, Old ksh does not support$()
inPS1
. So if it's a modern version of ksh, I wantPS1
to use the function I created; if it's the old version, I use just$PWD
. -
Thorbjørn Ravn Andersen almost 9 yearsWell, you could have two versions of your configuration file (perhaps one generated from the other) and then distribute out the appropriate version to the machine in question?
-
Thorbjørn Ravn Andersen almost 9 yearsAnother approach could be to simply say "It is only this particular machine which has the problem - I'll find a file or environment variable or something else which only exists here (probably AIX or something anyway) and test for that instead".
-
Sildoreth almost 9 yearsWhy doesn't this check if
$KSH_VERSION
is non-blank first? On my Ubuntu machine, this prints "ksh93", yetKSH_VERSION
is set. -
cuonglm almost 9 yearsThis won't be distinguish between
lksh
andpdksh
in Debian Jessie. -
jlliagre almost 9 years@cuonglm I have no Jessie to test. Do you mean
lksh
andpdksh
can't be sorted out from theirKSH_VERSION
? -
cuonglm almost 9 yearsNo, I mean running
strings
on them.KSH_VERSION
definitively can. -
jlliagre almost 9 years@cuonglm Sorry if I was unclear. When I wrote « for "real"
ksh
releases », I was explicitly excluding non AT&T ksh clones likepdksh
,mksh
andlksh
. -
Sildoreth almost 9 years@Gilles what if this was performed on the value returned by
command -v $0
? -
Gilles 'SO- stop being evil' almost 9 years@Sildoreth How is
$0
useful here? It designates the script, not to the interpreter. -
Sildoreth almost 9 years@Gilles Oh. I was thinking .kshrc. But I see what you mean.
-
jlliagre almost 9 years@cuonglm I updated my reply with a method that works with the
strings
command withlksh
andpdksh
too. -
cuonglm almost 9 years@jlliagre: If script was called with
ksh script
orlksh script
orpdksh script
, your solution failed. -
jlliagre almost 9 years@cuonglm Granted, indeed. One cannot reliably use the interpreter line, answer updated.
-
jlliagre almost 9 yearsThis would fail if some previously executed code (eg: .kshrc) tampered the KSH_VERSION variable with some random value.
-
cuonglm almost 9 years@jlliagre: No, as it was run as a script, it don't read
.kshrc
. -
jlliagre almost 9 yearsIf the
ENV
variable is set (and typically it is set to~/.kshrc
), the script will definitely read the.kshrc
file. Of course, it would be quite odd for a script to set a bogus KSH_VERSION but this is nevertheless possible, just like explicitly executing a script with a different interpreter than the one specified in its first line is a possible situation. -
cuonglm almost 9 years@jlliagre: Even you can change it, you will get segfault when you reference to
KSH_VERSION
. And inmksh
,pdksh
,lksh
,KSH_VERSION
is marked as readonly. -
jlliagre almost 9 years
pdksh
and derived shells are indeed protected by having theKSH_VERSION
variable readonly but AT&Tksh
ones are not. Releases older thanksh93t
(2008) don't set it and accept it as a variable name with no restriction. Any script based on KSH_VERSION can easily be fooled if that variable has previously been exported or is set in a.kshrc
file.ksh93t
and newer still do not segfault if this variable is set by an initialization script.ksh93t
toksh93u
only segfault it the variable is already set in their starting environment. Segfaulting was a bug that has been fixed inksh93v
. -
Sildoreth almost 9 yearsI don't see any difference when using a subshell. It still treats
${.sh.version}
as a syntax error that cannot be reconciled. The message I get isbad substitution
. -
Gilles 'SO- stop being evil' almost 9 years@sil The point of using a subshell is to catch the error. Redirect errors to
/dev/null
and ignore the exit status. -
Sildoreth almost 9 yearsI understand what you're saying. What I'm saying is that the error doesn't redirect. It always prints to the console. I tried this in Solaris, AIX, and HP-UX; and ksh exhibits this behavior in all of them.
-
Gilles 'SO- stop being evil' almost 9 years@Sildoreth Ah. I'd only tested on Linux, and I don't have any of these OSes to test now. Does
eval '_sh_version=$(echo "${.sh.version}")' 2>/dev/null
work any better? -
Sildoreth almost 9 yearsThat's a little better. It works prefectly in Solaris and HP-UX. For AIX, it works at the command line but curiously starts failing again if I try to place it in a shell function.
-
cuonglm almost 9 yearsWhy don't you use
${.sh.version}
? -
Sildoreth almost 9 years@cuonglm because I can't. See the comments on Gilles' answer.
-
SailorCire almost 9 yearsI haven't seen
strings
used in a while. It is a good program. Thanks for reminding me of it! -
Stéphane Chazelas over 8 yearsBetter as
command eval '_sh_version=${.sh.version}' 2> /dev/null
or{ _sh_version=...; } 2> /dev/null
-
Jeff Schaller about 7 yearsthis was the only one that worked for an AIX ksh 88f version.
-
bgStack15 almost 7 yearsI got the <kbd>ESC</kbd> , <kbd>CTRL</kbd>+<kbd>V</kbd> option to work, after I ran
set -o vi
to set the keybindings to be vi-like. Before that, or with +o vi or -o emacs, it simply would not show me. PD KSH v5.2.14 99/07/13.2 on openbsd 6.1 -
Greg A. Woods over 6 yearsUnfortunately NetBSD sh (though not dash-0.5.7-3 on linux) will outright stop reading the script when it sees
${.sh.version}
with the complaintSyntax error: Bad substitution
. -
Greg A. Woods over 6 yearsunfortunately the
whence
in Zsh has-a
-
Greg A. Woods over 6 yearsmodern AT&T Ksh still supplies
.sh.version
(indeedKSH_VERSION
is effectively an alias for it). Also some shells, e.g. NetBSD sh, simply stop reading after encountering${.sh.version}
and no amount of redirection can keep them running the script. -
Gilles 'SO- stop being evil' over 6 years@GregA.Woods Ouch. Can you try
_sh_version=$(echo "${.sh.version}"; true) 2>/dev/null
or_sh_version=$(eval 'echo "${.sh.version}"') 2>/dev/null
? -
Sildoreth over 6 years@GregA.Woods, this function is specifically for ksh. The function definition would go in .kshrc and so would not even exist for other shells such as zsh. zsh has its own built-in
whence
command which is in no way tied to ksh or the version thereof. I don't even know why you'd care to check if ksh is an old version from within an instance of zsh, which is a completely different shell. -
Greg A. Woods over 6 years
eval
to the rescue again! The redirection has to be moved into the command expansion though:_sh_version=$(eval 'echo "${.sh.version}"' 2>/dev/null)
Thank you very much! -
Greg A. Woods over 6 yearsThere is a problem with your assumptions: Zsh is often installed with a link to
/bin/ksh
, e.g. on Debian Linux. Now I don't use it there (and at the moment I can't change my login shell to check), so I don't know if it reads.kshrc
or not, but I would suspect it does. -
Greg A. Woods over 6 yearsFYI, my
eval
version even works with ksh88e (which I just got to build and run on NetBSD/amd64, albeit with some gross hacks and minor fixes). I.e. it "works" in the sense that it leaves_sh_version
empty and does not write anything to standard error; since as you know that version and those before it didn't have any way other than an editor command to show their version identification. -
Stéphane Chazelas over 6 yearsWhy the second
2> /dev/null
? -
Sildoreth over 6 years@Greg do you mean that "zsh" is set as a symbolic link to run ksh? If so, then you're not running zsh at all. And if that version of ksh supports
-a
, then that necessarily means that it's a modern version ofksh
; and theis_modern_ksh
function is just doing its job. -
Sildoreth over 6 years@Greg Or do you mean that zsh calls into ksh for some of its functionality? If so, I doubt you even need this function. My main reason for needing a function like this was the need to know when to work around a lack of specific functionality in old versions of ksh. Is there a specific shortcoming of old ksh versions that is bleeding into zsh for you? If so, I'd be surprised since the founding principle of zsh is that it combines the best of all other shells while discarding all of the shortcomings.
-
Greg A. Woods over 6 yearsNo, Zsh pretends to be Ksh, though until the most recent releases it did so rather poorly in some situations.
-
Sildoreth over 6 yearsWhy would you want to run this function for shells that aren't ksh? If you're running a script in bash or zsh, then ksh never comes into play. Furthermore, it's already been established via others' answers that
${.sh.version}
cannot be part of the solution because certain versions of ksh – the versions that the original post was concerned about – error fatally on that syntax. -
Greg A. Woods over 6 yearsAs I said, the function I show has been tested with versions of ksh that give "fatal" errors, as well as versions of Ash that do the same.
-
Greg A. Woods over 6 yearsScripts I write are intended to be portable and to be run by any capable shell. Also, as I said elsewhere, some folks won't necessarily know they're using Zsh as Ksh because when they type 'ksh' the Zsh binary will be invoked (with argv[0] as "ksh").
-
Sildoreth over 6 yearsThat does shed light on where you're coming from. However, that sounds like an unrealistic requirement. Typically, when a Unix developer says "portable", they don't mean "this code will run in any shell", they mean "this will run on any system". And if you need to execute a script that was written for another shell, that is perfectly legal; just start a non-interactive instance of the other shell in your script. I bring this up because I want to promote good coding practices. If this solution works for you, great. But I would advise others to take a simpler approach.
-
Greg A. Woods over 6 yearsI've been writing portable shell scripts that are intended to work with any Bourne (and later POSIX) compatible
/bin/sh
for the past 28 years. :-) -
Greg A. Woods over 6 yearsAdmittedly any efforts to drag backwards compatibility too far into the past are rather silly. I've only compiled versions of ancient AT&T Ksh and Unix Sh to satisfy my own personal desire to better understand the history and evolution of some features and to refresh my memory of how things were (which usually surprises me, as things were often much "better" than I remember, though sometime they were also much worse).
-
Michael Brux about 6 yearsDoes not work with mksh:
mksh: whence: -a: unknown option
. KSH_VERSION is:@(#)MIRBSD KSH R54 2016/11/11
(on Debian 9 / stretch) -
Sildoreth about 6 years@MichaelBrux The whole point is for an error to be thrown for older versions, which the error redirection then consumes. That's how this function identifies the ksh version as "old". Are you saying that the error redirection isn't working?
-
Michael Brux about 6 years@Sildoreth: "mksh is a successor of pdksh" - means, from my understanding, it is modern. Your code returns "OLD version", what is an error, from my point of view. Reason for this error is: mksha whence builtin has no -a option. Is that more clear / understandable / correct?