#! /bin/sh # vim:et:ft=sh:sts=2:sw=2 # # Versions determines the versions of all installed shells. # # Copyright 2008-2017 Kate Ward. All Rights Reserved. # Released under the Apache 2.0 License. # # Author: kate.ward@forestent.com (Kate Ward) # https://github.com/kward/shlib # # This library provides reusable functions that determine actual names and # versions of installed shells and the OS. The library can also be run as a # script if set executable. # # Disable checks that aren't fully portable (POSIX != portable). # shellcheck disable=SC2006 setglobal ARGV0 = $[basename $0] setglobal LSB_RELEASE = ''/etc/lsb-release'' setglobal VERSIONS_SHELLS = '"ash /bin/bash /bin/dash /bin/ksh /bin/pdksh /bin/sh /bin/zsh'" true; setglobal TRUE = $Status false; setglobal FALSE = $Status setglobal ERROR = '2' setglobal UNAME_R = $[uname -r] setglobal UNAME_S = $[uname -s] setglobal __versions_haveStrings = $(ERROR) proc versions_osName { setglobal os_name_ = ''unrecognized'' setglobal os_system_ = $(UNAME_S) setglobal os_release_ = $(UNAME_R) match $(os_system_) { with CYGWIN_NT-* setglobal os_name_ = ''Cygwin'' with Darwin setglobal os_name_ = $[/usr/bin/sw_vers -productName] setglobal os_version_ = $[versions_osVersion] match $(os_version_) { with 10.4|10.4.[0-9]* setglobal os_name_ = ''Mac OS X Tiger'' with 10.5|10.5.[0-9]* setglobal os_name_ = ''Mac OS X Leopard'' with 10.6|10.6.[0-9]* setglobal os_name_ = ''Mac OS X Snow Leopard'' with 10.7|10.7.[0-9]* setglobal os_name_ = ''Mac OS X Lion'' with 10.8|10.8.[0-9]* setglobal os_name_ = ''Mac OS X Mountain Lion'' with 10.9|10.9.[0-9]* setglobal os_name_ = ''Mac OS X Mavericks'' with 10.10|10.10.[0-9]* setglobal os_name_ = ''Mac OS X Yosemite'' with 10.11|10.11.[0-9]* setglobal os_name_ = ''Mac OS X El Capitan'' with 10.12|10.12.[0-9]* setglobal os_name_ = ''macOS Sierra'' with 10.13|10.13.[0-9]* setglobal os_name_ = ''macOS High Sierra'' with * setglobal os_name_ = ''macOS'' } with FreeBSD setglobal os_name_ = ''FreeBSD'' with Linux setglobal os_name_ = ''Linux'' with SunOS if grep 'OpenSolaris' /etc/release >/dev/null { setglobal os_name_ = ''OpenSolaris'' } else { setglobal os_name_ = ''Solaris'' } } echo $(os_name_) unset os_name_ os_system_ os_release_ os_version_ } proc versions_osVersion { setglobal os_version_ = ''unrecognized'' setglobal os_system_ = $(UNAME_S) setglobal os_release_ = $(UNAME_R) match $(os_system_) { with CYGWIN_NT-* setglobal os_version_ = $[expr $(os_release_) : '\([0-9]*\.[0-9]\.[0-9]*\).*] with Darwin setglobal os_version_ = $[/usr/bin/sw_vers -productVersion] with FreeBSD setglobal os_version_ = $[expr $(os_release_) : '\([0-9]*\.[0-9]*\)-.*] with Linux if test -r $(LSB_RELEASE) { if grep -q 'DISTRIB_ID=Ubuntu' $(LSB_RELEASE) { # shellcheck disable=SC2002 setglobal os_version_ = $[cat $(LSB_RELEASE) \ |awk -F= '$1~/DISTRIB_DESCRIPTION/{print $2}' \ |sed 's/"//g;s/ /-/g] } } elif test -r '/etc/redhat-release' { setglobal os_version_ = $[cat /etc/redhat-release] } with SunOS if grep 'OpenSolaris' /etc/release >/dev/null { setglobal os_version_ = $[grep 'OpenSolaris' /etc/release |awk '{print $2"("$3")"}] } else { setglobal major_ = $[echo $(os_release_) |sed 's/[0-9]*\.\([0-9]*\)/\1/] setglobal minor_ = $[grep Solaris /etc/release |sed 's/[^u]*\(u[0-9]*\).*/\1/] setglobal os_version_ = ""$(major_)$(minor_)"" } } echo $(os_version_) unset os_name_ os_release_ os_version_ major_ minor_ } proc versions_shellVersion { setglobal shell_ = $1 setglobal shell_present_ = $(FALSE) match $(shell_) { with ash test -x '/bin/busybox' && setglobal shell_present_ = $(TRUE) with * test -x $(shell_) && setglobal shell_present_ = $(TRUE) } if test $(shell_present_) -eq $(FALSE) { echo 'not installed' return ${FALSE} } setglobal version_ = '''' match $(shell_) { with */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_}` with ash setglobal version_ = $[versions_shell_ash $(shell_)] with */bash setglobal version_ = $[versions_shell_bash $(shell_)] with */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. setglobal version_ = $[versions_shell_dash] with */ksh setglobal version_ = $[versions_shell_ksh $(shell_)] with */pdksh setglobal version_ = $[versions_shell_pdksh $(shell_)] with */zsh setglobal version_ = $[versions_shell_zsh $(shell_)] with * setglobal version_ = ''invalid'' } echo $(version_:-unknown) unset shell_ version_ } # The ash shell is included in BusyBox. proc versions_shell_ash { busybox --help |head -1 |sed 's/BusyBox v\([0-9.]*\) .*/\1/' } proc versions_shell_bash { $1 --version !2 > !1 |grep 'GNU bash' |sed 's/.*version \([^ ]*\).*/\1/' } proc versions_shell_dash { eval dpkg >/dev/null !2 > !1 test $Status -eq 127 && return # return if dpkg not found dpkg -l |grep ' dash ' |awk '{print $3}' } proc versions_shell_ksh { setglobal versions_shell_ = $1 setglobal versions_version_ = '''' # Try a few different ways to figure out the version. if setglobal versions_version_ = $[$(versions_shell_) --version : !2 > !1] { setglobal versions_version_ = $[echo $(versions_version_) \ |sed 's/.*\([0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]\).*/\1/] } if test -z $(versions_version_) { _versions_have_strings setglobal versions_version_ = $[strings $(versions_shell_) !2 > !1 \ |grep Version \ |sed 's/^.*Version \(.*\)$/\1/;s/ s+ \$$//;s/ /-/g] } if test -z $(versions_version_) { setglobal versions_version_ = $[versions_shell_pdksh $(versions_shell_)] } echo $(versions_version_) unset versions_shell_ versions_version_ } proc versions_shell_pdksh { _versions_have_strings strings $1 !2 > !1 \ |grep 'PD KSH' \ |sed -e 's/.*PD KSH \(.*\)/\1/;s/ /-/g' } proc versions_shell_zsh { setglobal versions_shell_ = $1 # Try a few different ways to figure out the version. # shellcheck disable=SC2016 setglobal versions_version_ = $[echo 'echo ${ZSH_VERSION}' |$(versions_shell_)] if test -z $(versions_version_) { setglobal versions_version_ = $[$(versions_shell_) --version !2 > !1 |awk '{print $2}] } echo $(versions_version_) unset versions_shell_ versions_version_ } # Determine if the 'strings' binary installed. proc _versions_have_strings { test $(__versions_haveStrings) -ne $(ERROR) && return if eval strings /dev/null >/dev/null !2 > !1 { setglobal __versions_haveStrings = $(TRUE) return } echo 'WARN: strings not installed. try installing binutils?' > !2 setglobal __versions_haveStrings = $(FALSE) } proc versions_main { # Treat unset variables as an error. set -u setglobal os_name = $[versions_osName] setglobal os_version = $[versions_osVersion] echo "os: $(os_name) version: $(os_version)" for shell in [$(VERSIONS_SHELLS)] { setglobal shell_version = $[versions_shellVersion $(shell)] echo "shell: $(shell) version: $(shell_version)" } } if test $(ARGV0) = 'versions' { versions_main @Argv }