シェルバージョンを実行するPOSIX互換/クロスシェル方法は何ですか?

シェルバージョンを実行するPOSIX互換/クロスシェル方法は何ですか?

実行中のシェルのバージョン番号を取得するためにすべてのシェルで動作するPOSIX互換の方法または方法はありますか?

操作できるように名前/バイナリをインポートできますが、非常に単純なシェル(Travis-CI / Ubuntu Trustyでテスト済み)でも機能しません。$SHELL ps -p$$ -ocmd=$SHELL --version $(ps -p$$ -ocmd=) --versionsh

ほとんどのシェルに機能する方法はありますか(たとえば、caseシェルを区別する必要がある場合)。

編集する:これは、情報提供の目的でのみ使用されるシェルバージョンを印刷するために使用されます。はいいいえ戻り値で操作を実行するには、その値をユーザーに表示するだけです。

答え1

--version定義されたフラグはありません。shユーティリティ用POSIX規格。どのようなバージョン情報も含む標準環境やシェル変数はありません。これはシェルが不均一であることを意味します。必要そのバージョンを利用可能にしてください。

特定のバージョンのシェルをテストする必要がある場合、スクリプトはPOSIXと互換性がない可能性が高いため、質問は関係ありません。

kshシェルについては、以下を参照してください。kshバージョンを安全に入手するには?

また関連:端末で使用しているシェルをどのようにテストしますか?

答え2

残念ながら、POSIX sh標準の現在のバージョンは、シェルバージョンへの信頼性の高いアクセスのための公式のコマンドラインフラグや環境変数を提供していません。

幸いにも解決策があります。ほとんどのbash派生(dashとposhを除く)の場合、--versionこれをシェルに提供できます。またはecho "$BASH_VERSION"(bash)、echo "$ZSH_VERSION"(zsh)、echo "${.sh.version}"(ksh)。

一部のパッケージはバイナリファイル名でバージョンを区別します。たとえば、Python v3のコマンドラインアプリケーション名はですpython3。シェルの現在のプロセス名には、メジャーバージョンやシェルのバイナリパス(たとえば、/bin/shより具体的なパスを指す)を含めることができます(例/bin/bash4.4.12:.産むようには思えません。

ただし、パッケージングシステムは関連するシェルパッケージのバージョン番号を提供できます。したがって、dpkg -l <shell>(Debianデリバティブ)、yum -l <shell>(RHELデリバティブ)、emerge -Opv <shell>(Gentoo)、pacman -Qi dash(Arch)、brew list dash(Homebrew)などを実行してください。問題のシェルがある場合、shそのシェルはおそらくcoreutilsパッケージで提供されるので、coreutils代わりにパッケージマネージャに問い合わせてくださいsh

RVM kin、cygwin、およびその他のUNIX様環境では、シェルを含むディレクトリの名前をシェルバージョンとして指定できます。したがって、シェルアプリケーションの絶対パスを取得し、名前がそこに表示されることを確認してください。

最後に、シェルはオペレーティングシステムによって簡単に提供できるため、uname -a; cat /etc/*release*シェルバージョンを追跡するための少なくともいくつかの識別子を提供できます。

これらすべてのコマンドがスクリプトからセミコロンにリンクされている場合は、特定のシェルのバージョンを識別するためのかなり包括的なnmapなどの強力なツールがあります。

答え3

実際に方法を見つけました。ハウジングテストユニットサンイット2自分でシェルスクリプトで書かれたのは、リポジトリ「」には、使用されているシェルのバージョンを検出するコードも含まれています。

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_
}

これは、シェルを使用していない場合にシェルバージョンを取得するのにも効果があるかもしれませんが、私のユースケースでは、単に使用したり、$BASH_VERSION同様のものを使用した方が効率的であることを認めています。

ライセンス付きApacheライセンス@kward さん。

答え4

テドンが言ったように:

シェルスクリプトを実行すると、使用中のシェル構文を使用するシェルの小さなサブセットでのみ実行されます。 – terdon 5時間前

あなたの答えに対する答え:

POSIX規格で作るとそうではありません。その後、どのシェルでも実行できるはずです...

ポイントはそこにありますはいシェルバージョンを取得するPOSIXの指定方法はありません。したがって、シェルスクリプトのバージョンを確認すると、もはや厳密に言えばPOSIXではありません。

POSIXは次のセットであることを覚えておいてください。仕様。

本当に欲しいものがバージョンに応じたデバッグであれば、各シェルに別々の方法を提供する必要があるかもしれません。実装する あなたが目指しているものは何ですか、さまざまな条件付き検証と経験的な方法を使用しています。しかし、将来のシェルでも動作するという保証はありません。これらのシェルもPOSIXと完全に互換性があります。

あなたができる最高のことはヒューリスティックです。シェルを最初にリストすることをお勧めします実装するバージョンチェックのサポートに興味がある場合は、各バージョンのマニュアルページを確認して、そのシェルバージョンを入手する方法を学びます。多くのテストが必要です。同様の構文を使用するシェルをサポートするには、sh興味のあるシェル実装の以前のバージョンを確認することを忘れないでください。


開発しているソフトウェアの種類によっては、依存関係リストに「bashバージョン3+」を追加して完了する方が良いかもしれません。

関連情報