쉘 버전을 실행하는 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그냥 사용하거나 유사한 것을 사용하는 것이 더 효율적일 수 있음을 인정합니다.

라이센스가 있는아파치 라이센스@kward 님.

답변4

테든이 말했듯이:

쉘 스크립트를 실행하는 경우, 사용 중인 쉘 구문을 사용하는 쉘의 작은 하위 집합에서만 실행됩니다. – terdon 5시간 전

귀하의 답변에 대한 답변:

POSIX 규격으로 만들면 그렇지 않습니다. 그러면 어떤 쉘에서도 실행될 수 있어야합니다 ...

요점은 거기에 있습니다셸 버전을 얻는 POSIX 지정 방법은 없습니다. 따라서 쉘 스크립트의 버전을 확인하면 더 이상 엄밀히 말하면 POSIX가 아닙니다.

POSIX는 다음과 같은 집합이라는 것을 기억하세요.사양.

정말로 원하는 것이 버전에 따른 디버깅이라면 각 셸마다 별도의 방법을 제공해야 할 수도 있습니다.구현하다 당신이 목표로 삼고 있는 것이 무엇인지, 다양한 조건부 확인 및 경험적 방법을 사용합니다. 하지만 미래의 쉘에서도 작동할 것이라는 보장은 없습니다.이러한 쉘도 POSIX와 완벽하게 호환됩니다.

당신이 할 수 있는 가장 좋은 일은 휴리스틱입니다. 쉘을 먼저 나열하는 것이 좋습니다구현하다버전 확인 지원에 관심이 있는 경우 각 버전의 매뉴얼 페이지를 확인하여 해당 셸 버전을 얻는 방법을 알아보세요. 많은 테스트가 필요합니다. 유사한 구문을 사용하는 셸을 지원하려면 sh관심 있는 셸 구현의 이전 버전을 확인하는 것을 잊지 마세요.


개발 중인 소프트웨어 유형에 따라 종속성 목록에 "bash 버전 3+"를 추가하고 완료하는 것이 더 나을 수도 있습니다.

관련 정보