如何安全获取ksh的版本?


12

如何从ksh脚本中安全地获取ksh的版本?

我已经看到以下解决方案

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

在适当的情况下,所有这些方法都可以正常工作。但是,我关心的是不完美的情况。

具体来说,我使用的几台机器具有较旧的ksh版本,出于我的目的,这些版本严重缺乏功能。无论如何,我要(以编程方式)检查版本的原因是要查看ksh版本是否为功能较弱的版本之一。如果是这样,我想用不太出色的代码执行分支。

但是,在有问题的机器上,shell的能力不足导致无法检查版本...

  • 如果我尝试ksh --version,它什么也不打印,并打开一个新实例ksh
  • 如果尝试echo ${.sh.version}ksh将其视为语法错误,不能使用丢弃2> /dev/null

    $ echo ${.sh.version} 2> /dev/null  
    ksh: ${.sh.version}: bad substitution
    
  • 当然echo $KSH_VERSION看起来可以正常工作-我的意思是它不会崩溃-尽管在这些机器上它是空白的。另外,我看到的地方KSH_VERSION只被设定pdksh

问题:

  • 如何安全地以ksh编程方式检查版本?就我的目的而言,我并不在乎实际的版本号是多少,而只是它是否是的过时版本ksh
  • 是否$KSH_VERSION足够好?我的意思是,如果空白,那么ksh一定是过时的版本吗?该其他论坛是否正确,即使对于较新版本的,也可能未设置ksh
  • 根本没有办法检查吗?

1
有什么理由想要两个代码路径,而不仅仅是一个不那么出色的代码?
托尔比约恩Ravn的安徒生

@ThorbjørnRavnAndersen与提示有关。在我的.kshrc文件中,我有一个模拟tcsh和zsh提示的pwd缩写功能的函数,并且我设置PS1为使用此函数。然而,旧的ksh不支持$()PS1。因此,如果它是ksh的现代版本,我想PS1使用我创建的功能;如果是旧版本,我只使用$PWD
Sildoreth

好吧,您可能有两个版本的配置文件(也许一个版本是从另一个版本生成的),然后将适当的版本分发给有问题的计算机?
托尔比约恩Ravn的安徒生

另一种方法可能是简单地说:“只有这台特定的机器有问题-我将找到文件或环境变量或仅在此处存在的其他内容(可能是AIX或其他内容),然后进行测试”。
托尔比约恩Ravn的安徒生

Answers:


7

我认为.sh.version自ATT ksh 93的第一版以来就存在。它在pdksh或mksh中不可用。由于${.sh.version}是ksh93以外的shell中的语法错误,因此将其测试包装在子shell中并保护在后面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 最初是在公共领域的ksh克隆(pdksh)中创建的,并且相对较新地在2008年用ksh93t添加到了实际的Korn Shell中。

而不是测试版本号,您应该测试给您带来痛苦的特定功能。可以通过在子shell中尝试某些构造并查看它是否触发错误来测试大多数功能。


使用subshel​​l时,我看不出任何区别。仍将其视为${.sh.version}无法协调的语法错误。我收到的消息是bad substitution
Sildoreth 2015年

@sil使用子shell的目的是捕获错误。将错误重定向到/dev/null并忽略退出状态。
吉尔斯(Gilles)'所以

我明白你在说什么。我的意思是错误不会重定向。它总是打印到控制台。我在Solaris,AIX和HP-UX中尝试过;而ksh在所有这些中都表现出这种行为。
Sildoreth

@Sildoreth啊。我只在Linux上进行过测试,现在没有任何要测试的OS。是否eval '_sh_version=$(echo "${.sh.version}")' 2>/dev/null工作更好?
吉尔斯(Gilles)'所以

好一点。它完全可以在Solaris和HP-UX中工作。对于AIX,它可以在命令行运行,但是奇怪的是,如果我尝试将其放置在shell函数中,它将再次失败。
Sildoreth

6

KSH_VERSIONksh9393t版本之前未实现。它将设置在mksh,,pdkshlksh。因此,为了检查的版本ksh,我们可以尝试以下步骤:

  • 检查KSH_VERSION检测mkshpdkshlksh
  • 如果第一步失败,请尝试使用ksh93和之间不同的功能ksh88/86让David Korn向我们展示)。

考虑到这些,我将选择:

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

为什么不先检查是否$KSH_VERSION非空白?在我的Ubuntu计算机上,此打印为“ ksh93”,但KSH_VERSION已设置。
Sildoreth

如果某些先前执行的代码(例如:.kshrc)使用某个随机值篡改了KSH_VERSION变量,则此操作将失败。
jlliagre 2015年

@jlliagre:不,因为它是作为脚本运行的,所以不会读取.kshrc
cuonglm

如果ENV设置了变量(通常将其设置为~/.kshrc),则脚本肯定会读取.kshrc文件。当然,脚本设置伪造的KSH_VERSION是很奇怪的,但是这是可能的,就像使用不同于第一行中指定的解释器的脚本显式执行脚本一样。
jlliagre 2015年

@jlliagre:即使您可以更改它,引用也会遇到段错误KSH_VERSION。而在mkshpdkshlkshKSH_VERSION被标记为只读。
cuonglm

5

对于“实际” ksh发行版(即基于AT&T的发行版),我使用以下命令:

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

这是我得到的各种输出:

原始ksh:

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

dtksh;

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

现代ksh93:

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

对于pdksh/ msh ksh克隆和现代AT&T ksh版本,这也是可行的:

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

编辑:

我忽略了您是在脚本中问这个问题,而不是知道测试的ksh二进制文件的路径。

假设您确实想要使用的版本ksh,而不是它所支持的功能,这是仅使用strings至少在Linux和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)

请注意,此方法不可靠,/proc可能无法安装,并且肯定还有其他缺点。未经其他Unix操作系统测试。


这在Debian Jessie lkshpdkshDebian Jessie 之间是无法区分的。
cuonglm

@cuonglm我没有Jessie要测试。您的意思是lkshpdksh不能从他们那里挑选出来KSH_VERSION吗?
jlliagre 2015年

不,我是说strings在他们身上奔跑。KSH_VERSION绝对可以。
cuonglm

@cuonglm对不起,如果我不清楚。当我写了«为‘真正的’ ksh版本»,我被明确排除非AT&T ksh的克隆一样pdkshmkshlksh
jlliagre 2015年

strings在某些ksh二进制文件上运行是一个坏主意,因为您不知道这是否在运行脚本。也许您的脚本是由/usr/local/bin/kshor或/home/bob/bin/kshor /bin/shor /usr/posix/bin/shor 运行的……
吉尔斯(Gills'SO-不要邪恶)'

2

在为编写脚本时ksh,我注意到较早版本的似乎不支持-aksh的内置whence命令选项ksh。在我检查过的所有系统上,包括Solaris,AIX,HP-UX和Linux,这似乎都是正确的。

因此,这里是作为ksh函数的解决方案:

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
}

以及使用方法:

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

你为什么不使用${.sh.version}
cuonglm

@cuonglm,因为我做不到。请参阅有关Gilles回答的评论。
Sildoreth

不幸的是,whence在Zsh中有-a
Greg A. Woods

@ GregA.Woods,此功能专门用于ksh。函数定义将包含在.kshrc中,因此对于其他shell(例如zsh)甚至不存在。zsh有其自己的内置whence命令,该命令与ksh或其版本无关。我什至都不知道为什么您要从zsh实例(完全不同的shell)中检查ksh是否为旧版本。
Sildoreth '17

您的假设存在一个问题:Zsh通常/bin/ksh在Debian Linux上带有指向的链接进行安装。现在我不在那儿使用它(目前无法更改我的登录shell进行检查),所以我不知道它是否读取.kshrc,但我怀疑它会读取。
格雷格·伍兹

1

CTRL+ ALT+V

要么

ESCCTRL+V

就交互式确定所使用的KSH版本而言,通常已被证明非常可靠,但是编写脚本的难度却更大。


1
这是唯一适用于AIX ksh 88f版本的版本。
杰夫·谢勒

1
在我set -o vi将键绑定设置为类似于vi 之后,我使<kbd> ESC </ kbd>,<kbd> CTRL </ kbd> + <kbd> V </ kbd>选项可以工作。在此之前,或者使用+ o vi或-o emacs,它根本不会显示给我。PD KSH v5.2.14 99/07 / 13.2 on openbsd 6.1
bgStack15 '17

0

我认为使用$ {。sh.version}的基本问题是ksh88只是停止,退出代码为非零。

因此,我的解决方案是将引用$ {。sh.version}的代码放在子外壳中,然后进行测试以查看子外壳是否退出非零值,并在子外壳中包含适用于以下版本的代码:引用$ {。sh.version}起作用的ksh。将其包装到一个函数中,然后再由另一个函数调用该函数来反转返回代码,以便最终调用检查是否为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"

我已经在AIX和Oracle Enterprise Linux 5和6以及ksh88,ksh93和pdksh上运行了它。

皮特


1
现代的AT&T Ksh仍在供应.sh.version(实际上实际上KSH_VERSION是别名)。同样,某些外壳程序(例如NetBSD sh)在遇到问题后只会停止读取${.sh.version},没有任何重定向可以使它们继续运行脚本。
格雷格·伍兹

0

以下代码似乎对我测试过的所有shell都相当有效,包括旧的ksh88e和几乎完整的常见Ksh克隆(尽管每个版本只有一个版本),尽管我尚未测试实际的原始Bourne shell(这样做可能需要调整test表达式以适应较旧的版本。

附录:

我现在还使用Heirloom Bourne Shell成功地对此进行了测试,尽管使用了外部(和更现代的)test程序。

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"

您为什么要对非ksh的shell运行此功能?如果您在bash或zsh中运行脚本,则ksh永远不会起作用。此外,已经通过其他人的答案建立了它,而该答案${.sh.version}不能成为解决方案的一部分,因为某些版本的ksh(原始帖子所关注的版本)在该语法上发生致命错误。
Sildoreth '17

就像我说的那样,我展示的功能已经过ksh版本的测试,该版本会产生“致命”错误,而Ash版本也可以进行测试。
格雷格·伍兹

我编写的脚本旨在可移植,并可由任何有能力的Shell运行。另外,正如我在其他地方所说的,有些人不一定知道他们将Zsh用作Ksh,因为当他们键入'ksh'时,将调用Zsh二进制文件(其中argv [0]为“ ksh”)。
格雷格·伍兹

这确实说明了您来自哪里。但是,这听起来像是不现实的要求。通常,当Unix开发人员说“可移植”时,它们的意思不是“该代码将在任何shell中运行”,而是指“此代码将在任何系统上运行”。而且,如果您需要执行为其他Shell编写的脚本,那完全合法。只需在脚本中启动另一个Shell的非交互式实例即可。之所以提出这一点,是因为我想促进良好的编码实践。如果该解决方案适合您,那就太好了。但我建议其他人采取更简单的方法。
Sildoreth

1
诚然,将向后兼容性拖到过去的任何努力都是很愚蠢的。我只编译了古代AT&T Ksh和Unix Sh的版本,以满足我自己的个人愿望,即可以更好地了解某些功能的历史和演变,并刷新我对事物的印象(这通常令我感到惊讶,因为事物通常很多,“比我记得的要好”,尽管有时它们也要差得多)。
格雷格·伍兹
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.