是否有一种 POSIX 兼容的方法或一种适用于所有 shell 的方法来获取正在运行的 shell 的版本号?
您可以获取名称/二进制文件,因此可能可以工作,但在非常简单的shell 中(在 Travis-CI/Ubuntu Trusty 中测试),这也不起作用。$SHELL
ps -p$$ -ocmd=
$SHELL --version
$(ps -p$$ -ocmd=) --version
sh
有没有一种方法,或者至少是一种简短/简单的方法,适用于大多数 shell(例如,如果您必须用来case
区分 shell)?
编辑:这仅用于打印所使用的 shell 的版本,即仅用于提供信息的目的。我愿意不是想要对返回值做一些事情,只需将其显示给用户即可。
答案1
没有--version
定义标志sh
实用程序的 POSIX 标准。也没有包含任何类型版本信息的标准环境变量或 shell 变量。这意味着外壳甚至不需要使其版本可用。
如果您需要测试特定版本的 shell,那么您的脚本不太可能符合 POSIX,因此问题(就目前情况而言)变得无关紧要。
对于ksh
外壳,请参见如何安全地获取 ksh 版本?
答案2
不幸的是,当前版本的 POSIX sh 标准既不提供官方命令行标志,也不提供用于可靠访问 shell 版本的环境变量。
幸运的是,有解决方法。对于大多数 bash 衍生品(dash 和 posh 除外),可以尝试--version
向 shell 提供。或者echo "$BASH_VERSION"
(bash)、echo "$ZSH_VERSION"
(zsh)、echo "${.sh.version}"
(ksh)。
有些包在二进制文件名中区分版本,例如 Python v3 的命令行应用程序名称是python3
. shell 的当前进程名称可能包含主要版本,或者 shell 的二进制路径(例如/bin/sh
指向更具体路径(例如/bin/bash4.4.12
.不幸的是,这个约定还没有进入 shell 开发人员的小社区,因此这些检查不太可能产生有用的结果。
然而,打包系统可能会提供相关 shell 包的版本号。所以运行dpkg -l <shell>
(Debian 衍生品)、yum -l <shell>
(RHEL 衍生品)、emerge -Opv <shell>
(Gentoo)、pacman -Qi dash
(Arch)、brew list dash
(Homebrew)等。如果有问题的 shell 是sh
,那么该 shell 可能是由 coreutils 包提供的,因此请查询包管理器而coreutils
不是sh
。
对于 RVM kin、cygwin 和其他类 UNIX 环境,包含 shell 的目录可能会命名 shell 的版本。因此,获取 shell 应用程序的绝对路径并查看名称是否出现在那里。
最后,外壳可以简单地由操作系统提供,因此uname -a; cat /etc/*release*
可以提供至少某种用于跟踪外壳版本的标识符。
如果所有这些命令在脚本中都用分号连接起来,那么人们将拥有一个相当全面的、类似于 nmap 的强力工具来识别给定 shell 的版本。
答案3
我其实找到了办法。外壳测试单元舒尼特2,本身编写为 shell 脚本,有一个“版本库”,其中还有检测所使用的 shell 版本的代码:
VERSIONS_SHELLS="ash /bin/bash /bin/dash /bin/ksh /bin/pdksh /bin/sh /bin/zsh"
versions_shellVersion() {
shell_=$1
shell_present_=${FALSE}
case "${shell_}" in
ash)
[ -x '/bin/busybox' ] && shell_present_=${TRUE}
;;
*)
[ -x "${shell_}" ] && shell_present_=${TRUE}
;;
esac
if [ ${shell_present_} -eq ${FALSE} ]; then
echo 'not installed'
return ${FALSE}
fi
version_=''
case ${shell_} in
*/sh)
# TODO(kward): fix this
## this could be one of any number of shells. try until one fits.
#version_=`versions_shell_bash ${shell_}`
## dash cannot be self determined yet
#[ -z "${version_}" ] && version_=`versions_shell_ksh ${shell_}`
## pdksh is covered in versions_shell_ksh()
#[ -z "${version_}" ] && version_=`versions_shell_zsh ${shell_}`
;;
ash) version_=`versions_shell_ash ${shell_}` ;;
*/bash) version_=`versions_shell_bash ${shell_}` ;;
*/dash)
# simply assuming Ubuntu Linux until somebody comes up with a better
# test. the following test will return an empty string if dash is not
# installed.
version_=`versions_shell_dash`
;;
*/ksh) version_=`versions_shell_ksh ${shell_}` ;;
*/pdksh) version_=`versions_shell_pdksh ${shell_}` ;;
*/zsh) version_=`versions_shell_zsh ${shell_}` ;;
*) version_='invalid'
esac
echo ${version_:-unknown}
unset shell_ version_
}
# The ash shell is included in BusyBox.
versions_shell_ash() {
busybox --help |head -1 |sed 's/BusyBox v\([0-9.]*\) .*/\1/'
}
versions_shell_bash() {
$1 --version 2>&1 |grep 'GNU bash' |sed 's/.*version \([^ ]*\).*/\1/'
}
versions_shell_dash() {
eval dpkg >/dev/null 2>&1
[ $? -eq 127 ] && return # return if dpkg not found
dpkg -l |grep ' dash ' |awk '{print $3}'
}
versions_shell_ksh() {
versions_shell_=$1
# try a few different ways to figure out the version
versions_version_=`${versions_shell_} --version : 2>&1`
if [ $? -eq 0 ]; then
versions_version_=`echo "${versions_version_}" \
|sed 's/.*\([0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]\).*/\1/'`
else
versions_version_=''
fi
if [ -z "${versions_version_}" ]; then
_versions_have_strings
versions_version_=`strings ${versions_shell_} 2>&1 \
|grep Version \
|sed 's/^.*Version \(.*\)$/\1/;s/ s+ \$$//;s/ /-/g'`
fi
if [ -z "${versions_version_}" ]; then
versions_version_=`versions_shell_pdksh ${versions_shell_}`
fi
echo ${versions_version_}
unset versions_shell_ versions_version_
}
versions_shell_pdksh() {
_versions_have_strings
strings $1 2>&1 \
|grep 'PD KSH' \
|sed -e 's/.*PD KSH \(.*\)/\1/;s/ /-/g'
}
versions_shell_zsh() {
versions_shell_=$1
# try a few different ways to figure out the version
versions_version_=`echo 'echo ${ZSH_VERSION}' |${versions_shell_}`
if [ -z "${versions_version_}" ]; then
versions_version_=`${versions_shell_} --version 2>&1 |awk '{print $2}'`
fi
echo ${versions_version_}
unset versions_shell_ versions_version_
}
当不使用 shell 时,这甚至可能可以获取 shell 的版本,但我承认在我的用例中,仅使用$BASH_VERSION
或类似的方法可能会更有效。
已获得许可阿帕奇许可证由@kward。
答案4
正如特登所说:
如果您正在运行 shell 脚本,那么它只会在一小部分 shell 上运行:那些使用您正在使用的任何 shell 语法的 shell。 – terdon 5 小时前
针对您的回复:
如果你让它兼容 POSIX,它就不会。那么它应该能够在任何 shell 上运行......
重点是有是没有 POSIX 指定的方式来获取 shell 的版本。因此,如果您检查 shell 脚本的版本,它已经不是严格的 POSIX。
请记住 POSIX 是一组规格。
如果您真正想要的是与版本相关的调试,则可能必须为每个 shell 提供单独的方法执行 您所瞄准的,带有一堆条件检查和启发式。但不能保证它适用于未来的 shell,即使这些 shell 完全符合 POSIX 规范。
你能做的最好的事情就是启发法。我建议你首先列出 shell实施您有兴趣支持版本检查,然后检查每个版本的手册页以了解如何获取该 shell 的版本。需要进行大量的测试;如果您确实想使用sh
类似语法支持任何 shell,请不要忘记检查感兴趣的 shell 实现的旧版本。
根据您正在开发的软件类型,您可能最好将“bash version 3+”添加到您的依赖项列表中并完成它。