#! / bin / sh vs #! / bin / bash para máxima portabilidade

35

Eu costumo trabalhar com servidores Ubuntu LTS que, pelo que entendi, symlink /bin/sh to /bin/dash . Muitas outras distribuições através do link simbólico /bin/sh para /bin/bash .

A partir disso, entendo que, se um script usa #!/bin/sh no topo, ele pode não ser executado da mesma maneira em todos os servidores?

Existe uma prática sugerida em qual shell usar para scripts quando se deseja a máxima portabilidade desses scripts entre servidores?

    
por cherouvim 30.07.2017 / 07:08

3 respostas

63

Existem aproximadamente quatro níveis de portabilidade para scripts de shell (no que diz respeito à linha shebang):

  1. Mais portável: use um #!/bin/sh shebang e use somente a sintaxe básica do shell especificada no Padrão POSIXO . Isso deve funcionar em praticamente qualquer sistema POSIX / unix / linux. (Bem, exceto o Solaris 10 e versões anteriores, que tinham o verdadeiro shell Bourne legado, precedendo o POSIX de modo não compatível, como /bin/sh .)

  2. Segundo mais portável: use uma linha shebang #!/bin/bash (ou #!/usr/bin/env bash ) e atenha-se aos recursos do bash v3. Isso funcionará em qualquer sistema que tenha bash (no local esperado).

  3. Terceiro mais portátil: use uma linha shebang #!/bin/bash (ou #!/usr/bin/env bash ) e use os recursos do bash v4. Isso falhará em qualquer sistema que tenha o bash v3 (por exemplo, macOS, que precisa usá-lo por motivos de licenciamento).

  4. Menos portável: use um #!/bin/sh shebang e use extensões bash para a sintaxe do shell POSIX. Isso falhará em qualquer sistema que tenha algo diferente de bash para / bin / sh (como versões recentes do Ubuntu). Nunca faça isso; não é apenas um problema de compatibilidade, é simplesmente errado. Infelizmente, é um erro que muitas pessoas cometem.

Minha recomendação: use o mais conservador dos três primeiros que fornece todos os recursos de shell necessários para o script. Para a portabilidade máxima, use a opção 1, mas, na minha experiência, alguns recursos bash (como arrays) são úteis o suficiente para que eu vá com o número 2.

A pior coisa que você pode fazer é # 4, usando o shebang errado. Se você não tiver certeza de quais recursos são POSIX básicos e quais são extensões bash, fique com o bash shebang (ou seja, a opção nº 2) ou teste o script completamente com um shell muito básico (como dash em seus servidores Ubuntu LTS). O wiki do Ubuntu tem uma boa lista de bashisms a serem observados .

Há algumas informações muito boas sobre a história e as diferenças entre os shells no Unix & Questão do Linux "O que significa ser compatível com sh?" / a> e a pergunta Stackoverflow "Diferença entre sh e bash" .

Além disso, esteja ciente de que o shell não é a única coisa que difere entre sistemas diferentes; Se você está acostumado com o Linux, você está acostumado com os comandos GNU, que têm muitas extensões fora do padrão que você pode não encontrar em outros sistemas Unix (por exemplo, bsd, macOS). Infelizmente, não há regra simples aqui, você só precisa saber o intervalo de variação dos comandos que está usando.

Um dos comandos mais desagradáveis em termos de portabilidade é um dos mais básicos: echo . Sempre que você usá-lo com alguma opção (por exemplo, echo -n ou echo -e ) ou com quaisquer escapes (barras invertidas) na sequência para imprimir, versões diferentes farão coisas diferentes. Toda vez que você quiser imprimir uma string sem um avanço de linha depois dela, ou com fugas na string, use printf (e aprenda como funciona - é mais complicado que echo é). O comando ps é também uma bagunça .

Outra coisa geral a ser observada são as extensões recentes / do GNU para a sintaxe da opção de comando: o formato de comando antigo (padrão) é que o comando é seguido por opções (com um único traço e cada opção é uma única letra) , seguido por argumentos de comando. As variantes recentes (e muitas vezes não portáteis) incluem opções longas (geralmente introduzidas com -- ), permitindo que as opções sejam exibidas após os argumentos e usando -- para separar as opções dos argumentos.

    
por 30.07.2017 / 08:37
5

No script ./configure que prepara a linguagem TXR para construção, escrevi o prólogo a seguir para uma melhor portabilidade. O script será autoinicializado mesmo se #!/bin/sh for um Bourne Shell antigo não em conformidade com POSIX. (Eu construo cada lançamento em uma máquina Solaris 10).

#!/bin/sh

# use your own variable name instead of txr_shell;
# adjust to taste: search for your favorite shells

if test x$txr_shell = x ; then
  for shell in /bin/bash /usr/bin/bash /usr/xpg4/bin/sh ; do
    if test -x $shell ; then
       txr_shell=$shell
       break
    fi
  done
  if test x$txr_shell = x ; then
    echo "No known POSIX shell found: falling back on /bin/sh, which may not work"
    txr_shell=/bin/sh
  fi
  export txr_shell
  exec $txr_shell $0 ${@+"$@"}
fi

# rest of the script here, executing in upgraded shell

A idéia aqui é que encontramos um shell melhor do que aquele em que estamos rodando e re-execute o script usando esse shell. A variável de ambiente txr_shell está definida, para que o script re-executado saiba que é a instância recursiva re-executada.

(No meu script, a variável txr_shell também é usada subseqüentemente, para exatamente duas finalidades: em primeiro lugar, é impressa como parte de uma mensagem informativa na saída do script. Em segundo lugar, ela é instalada como SHELL variável no Makefile , para que make use este shell também para executar receitas.)

Em um sistema em que /bin/sh é traço, você pode ver que a lógica acima encontrará /bin/bash e execute novamente o script com isso.

Em uma caixa do Solaris 10, o /usr/xpg4/bin/sh será ativado se nenhum Bash for encontrado.

O prólogo é escrito em um dialeto de shell conservador, usando test para testes de existência de arquivo e o truque ${@+"$@"} para expandir argumentos atendendo a alguns shells antigos quebrados (o que seria simplesmente "$@" se estivéssemos em um Shell em conformidade com POSIX).

    
por 31.07.2017 / 06:47
2

Todas as variações da linguagem shell Bourne são objetivamente terríveis em comparação com as linguagens de script modernas como Perl, Python, Ruby, node.js e até mesmo (sem dúvida) Tcl. Se você tiver que fazer alguma coisa, mesmo que seja um pouco complicado, você será mais feliz a longo prazo se usar uma das opções acima, em vez de um script de shell.

A primeira e única vantagem que a linguagem de shell ainda tem sobre essas novas linguagens é que algo chamando a si mesma /bin/sh é garantida para existir em qualquer coisa que pareça ser Unix. No entanto, esse algo pode nem ser compatível com POSIX; muitos dos Unixes proprietários legados congelaram a linguagem implementada por /bin/sh e os utilitários no PATH padrão às mudanças exigidas pelo Unix95 (sim, Unix95, há vinte anos e contando). Pode haver um conjunto de Unix95, ou mesmo POSIX.1-2001 se você tiver sorte, ferramentas em um diretório não no PATH padrão (por exemplo, /usr/xpg4/bin ), mas elas não são garantidas existe.

No entanto, o básico do Perl é mais provável estar presente em uma instalação Unix selecionada arbitrariamente do que o Bash. (Por "o básico do Perl" quero dizer /usr/bin/perl existe e é algum , possivelmente muito antigo, versão do Perl 5, e se você tiver sorte o conjunto de módulos que acompanham essa versão do o intérprete também está disponível.)

Portanto:

Se você está escrevendo algo que precisa funcionar em todos os lugares que se pretende UNIX (como um script "configure"), você precisa usar #! /bin/sh , e você não precisa usar nenhuma extensão. Hoje em dia eu escreveria shell compatível com POSIX.1-2001 nesta circunstância, mas eu estaria preparado para corrigir POSIXisms se alguém pedisse suporte para ferro enferrujado.

Mas se você não escreve algo que tem que funcionar em qualquer lugar, então no momento em que você é tentado a usar qualquer Bashism, você deve parar e reescrever a coisa toda em uma linguagem de script melhor. . Seu eu futuro vai agradecer.

(Então, quando é apropriado usar as extensões Bash? Na primeira ordem: nunca. Na segunda ordem: apenas para estender o ambiente interativo Bash - por exemplo, para fornecer preenchimento inteligente e avisos sofisticados. )

    
por 31.07.2017 / 17:58