POSIX-compatível / cross-shell maneira de obter a versão do shell em execução?

4

Existe uma maneira ou modo compatível com POSIX, que funciona em todos os shells, para obter o número da versão do shell, que está sendo executado?

Com $SHELL ps -p$$ -ocmd= , você pode obter o nome / binário, então $SHELL --version $(ps -p$$ -ocmd=) --version pode funcionar, mas em um nível muito simples sh shell (testado no Travis-CI / Ubuntu Trusty) isso não funciona nem um pouco.

Existe uma maneira ou, pelo menos, uma maneira curta / fácil, que funciona para a maioria dos shells (por exemplo, no caso de você precisar usar case para diferenciar entre os shells)?

Edit: Isto é apenas para imprimir a versão do shell que é usado, ou seja, apenas para fins informativos. Eu faço não quero fazer algo com o valor de retorno, apenas mostre para o usuário.

    
por rugk 18.09.2017 / 17:41

4 respostas

7

Não há sinalizador --version definido no padrão POSIX para o utilitário sh . Também não há variável de ambiente padrão ou variável de shell que contenha qualquer tipo de informação de versão. Isso significa que o shell nem precisa disponibilizar sua versão.

Se você precisar testar uma versão específica de um shell, provavelmente seu script não estará em conformidade com POSIX, portanto, a questão (como está) se torna irrelevante.

Para o shell ksh , consulte Como posso obter com segurança a versão do ksh?

Também relacionado: Como testar qual shell estou usando em um terminal?

    
por 18.09.2017 / 17:44
2

Infelizmente, a edição atual dos padrões sh POSIX não oferece um sinalizador oficial de linha de comando nem uma variável de ambiente para acessar de forma confiável a versão do shell.

Felizmente, existem soluções alternativas. Pode-se tentar fornecer --version para o shell, para a maioria dos derivados bash (exceto dash e posh). Ou echo "$BASH_VERSION" (bash), echo "$ZSH_VERSION" (zsh), echo "${.sh.version}" (ksh).

Alguns pacotes diferenciam versões no nome de arquivo binário, como o nome do aplicativo de linha de comando do Python v3 é python3 . É possível que o nome do processo atual de um shell inclua a versão principal ou o caminho binário de um shell como /bin/sh links para um caminho mais específico, como /bin/bash4.4.12 . Infelizmente, essa convenção não entrou na minúscula comunidade de desenvolvedores de shell, então é improvável que essas verificações produzam resultados úteis.

No entanto, o sistema de empacotamento pode apresentar um número de versão para o pacote de shell em questão. Portanto, execute dpkg -l <shell> (derivadas do Debian), yum -l <shell> (derivadas do RHEL), emerge -Opv <shell> (Gentoo), pacman -Qi dash (Arch), brew list dash (Homebrew) e assim por diante. Se o shell em questão for sh , esse shell provavelmente será fornecido pelo pacote coreutils, portanto, consulte o gerenciador de pacotes para coreutils em vez de sh .

Para RVM, cygwin e outros ambientes semelhantes a unix, o diretório que contém o shell pode nomear a versão do shell. Então pegue o caminho absoluto para o aplicativo shell e veja se o nome aparece lá.

Finalmente, o shell pode ser simplesmente fornecido pelo sistema operacional, portanto, uname -a; cat /etc/*release* pode fornecer pelo menos algum tipo de identificador para rastrear a versão do shell.

Se todos esses comandos forem unidos com ponto-e-vírgula em um script, seria necessário ter uma força bruta razoavelmente abrangente, uma ferramenta semelhante ao nmap para identificar a versão de um determinado shell.

    
por 24.09.2017 / 07:04
1

Como Terdon disse:

If you're running a shell script, it will only run on a small subset of shells anyway: those that use whatever shell syntax you're using. – terdon♦ 5 hours ago

Para responder à sua resposta:

If you make it POSIX-compatible, it won't. Then it should be able to run on any shell…

O ponto é que não há nenhuma maneira especificada pelo POSIX para obter a versão do shell. Então, se você tem uma checagem no seu shell script para a versão, ela já não é estritamente POSIX.

Tenha em mente que o POSIX é um conjunto de especificações .

Se o que você realmente quer é uma depuração relacionada à versão, provavelmente será necessário ter um método separado para cada implementação do shell que está segmentando , com um monte de verificações condicionais e heurísticas. Mas não será garantido que funcione em futuros shells, mesmo se esses shells estiverem em conformidade com as especificações do POSIX.

O melhor que você pode fazer é heurística. Eu sugiro que você inicie listando as implementações shell nas quais está interessado em dar suporte às suas verificações de versão, e então inspecione a man page para ver como obter a versão do shell. Muitos e muitos testes serão necessários; não se esqueça de verificar as versões antigas das implementações de shell de interesse, se você realmente quiser oferecer suporte a QUALQUER shell com uma sintaxe semelhante a sh .

Dependendo do tipo de software que você está desenvolvendo, talvez seja melhor adicionar apenas "bash versão 3+" à sua lista de dependências e terminar com isso.

    
por 18.09.2017 / 23:40
0

Eu realmente encontrei um caminho. A unidade de testes do shell shunit2 , escrita como scripts de shell, tem um " biblioteca de versões ", que também possui código para detectar a versão do shell usada:

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.]*\) .*//'
}

versions_shell_bash() {
  $1 --version 2>&1 |grep 'GNU bash' |sed 's/.*version \([^ ]*\).*//'
}

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]\).*//''
  else
    versions_version_=''
  fi

  if [ -z "${versions_version_}" ]; then
    _versions_have_strings
    versions_version_='strings ${versions_shell_} 2>&1 \
      |grep Version \
      |sed 's/^.*Version \(.*\)$//;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 \(.*\)//;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_
}

Isso pode até funcionar para obter a versão de um shell, quando não é usado, mas admito que no meu caso de uso apenas usando $BASH_VERSION ou similar, pode ser mais eficaz.

Licenciado sob a Licença Apache por @kward.

    
por 25.09.2017 / 12:34