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

cherouvim 09/03/2017. 3 answers, 2.821 views
linux bash shell sh

Eu costumo trabalhar com os servidores Ubuntu LTS que, do que eu entendo simbólico /bin/sh para /bin/dash . Muitas outras distros, porém simlink /bin/sh to /bin/bash .

A partir disso eu entendo que se um script usar #!/bin/sh na parte superior, ele pode não correr da mesma maneira em todos os servidores?

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

1 Comments
Thorbjørn Ravn Andersen 08/01/2017
Existem pequenas diferenças entre as várias conchas. Se a portabilidade for o mais importante para você, use #!/bin/sh e não use mais nada do que o shell original fornecido.

3 Answers


Gordon Davisson 08/01/2017.

Há cerca de quatro níveis de portabilidade para scripts de shell (na medida em que a linha de shebang está em causa):

  1. Mais portátil: use um #!/bin/sh shebang e use only a sintaxe básica do shell especificada no padrão POSIX . Isso deve funcionar em praticamente qualquer sistema POSIX / unix / linux. (Bem, exceto o Solaris 10 e anterior, que possuía o legado real Bourne shell, anterior à POSIX tão não compatível, como /bin/sh .)

  2. Segundo mais portátil: use uma linha de #!/bin/bash (ou #!/usr/bin/env bash ) e fique com os recursos do bash v3. Isso funcionará em qualquer sistema que tenha bash (no local esperado).

  3. Terceiro mais portátil: use uma linha #!/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, o MacOS, que deve usá-lo por motivos de licenciamento).

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

Minha recomendação: use o mais conservador dos três primeiros que fornece todos os recursos de shell que você precisa 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 # 2.

O pior que você pode fazer é # 4, usando o shebang errado. Se você não tiver certeza de quais recursos são básicos POSIX e quais são as extensões bash, coloque-se com um bash shebang (ou seja, opção # 2), ou teste o script com um shell muito básico (como a jogada em seus servidores Ubuntu LTS). O wiki do Ubuntu tem uma boa lista de bashis para cuidar .

Há algumas informações muito boas sobre a história e as diferenças entre os shells na pergunta Unix & Linux "O que significa ser compatível?" e a pergunta "Diferença entre sh e bash" de Stackoverflow.

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

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

Outra coisa geral para assistir é extensões recentes / GNUish para sintaxe de 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 apresentadas com -- ), permitindo que as opções venha após os argumentos e usando -- para separar as opções dos argumentos.

5 comments
12 pabouk 07/30/2017
A quarta opção é simplesmente uma idéia errada. Não use isso.
3 Gordon Davisson 07/30/2017
@pabouk eu concordo completamente, então eu editei minha resposta para tornar isso mais claro.
1 jlliagre 07/30/2017
Sua primeira afirmação é um pouco enganadora. O padrão POSIX não especifica nada sobre o comportamento do shebang dizendo que ele leva a um comportamento não especificado. Além disso, POSIX não especifica onde o shell posix deve estar localizado, apenas seu nome ( sh ), portanto, /bin/sh não é garantido para ser o caminho certo. O mais portátil é então não especificar qualquer shebang, ou para adaptar o shebang ao sistema operacional usado.
2 Michael Kjörling 07/30/2017
Eu caí no # 4 com um roteiro meu muito recentemente, e simplesmente não consegui descobrir por que não estava funcionando; Afinal, os mesmos comandos funcionaram de forma sólida, e fizeram exatamente o que eu queria que eles fizessem, quando eu tentá-los diretamente no shell. Assim que mudei #!/bin/sh para #!/bin/bash , o script funcionou perfeitamente. (Para minha defesa, esse script evoluiu ao longo do tempo de um que realmente só precisava de sh-isms, para um que dependia de um comportamento parecido com a da fuga).
2 jlliagre 07/30/2017
@ MichaelKjörling O (verdadeiro) shell Bourne quase nunca foi incluído nas distribuições Linux e não é compatível com POSIX de qualquer maneira. O shell padrão POSIX foi criado a partir do comportamento do ksh , não Bourne. O que a maioria das distribuições do Linux seguindo o FHS faz é ter /bin/sh sendo um link simbólico para o shell que eles selecionam para fornecer compatibilidade POSIX, geralmente bash ou dash .

Kaz 07/31/2017.

No script ./configure que prepara o idioma TXR para a construção, escrevi o seguinte prólogo para uma melhor portabilidade. O script será inicializado mesmo se #!/bin/sh for um Bourne Shell antigo não-POSIX-conforme. (Eu crio cada versão em uma VM 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 que aquele em que estamos executando, e re-execute o script usando esse shell. A variável de ambiente txr_shell está definida, de modo que o script re-executado sabe que é a instância recursiva re-executada.

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

Em um sistema onde /bin/sh é dash, você pode ver que a lógica acima encontrará /bin/bash e re-execute o script com isso.

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

O prólogo está escrito em um dialécato conservador, usando o test para testes de existência de arquivos e o truque ${@+"$@"} para expandir os argumentos atendendo a alguns shells quebrados (que simplesmente seriam "$@" se estivéssemos em um shell compatível com POSIX).

2 comments
Charles Duffy 07/31/2017
Não seria necessário o x hackery se houvesse citações adequadas, já que as situações que exigiam que o idioma envolvesse agora - invocações de teste obsoletas com -a ou -o combinando vários testes.
Kaz 07/31/2017
@CharlesDuffy De fato; O test x$whatever que test x$whatever que eu esteja perpetrando parece uma cebola no verniz. Se não pudermos confiar na cassete quebrada para fazer citações, então a tentativa final ${@+"$@"} é inútil.

zwol 07/31/2017.

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

A única e única vantagem que a linguagem de shell ainda possui nos idiomas mais recentes é que something que se chama /bin/sh é garantido para existir em qualquer coisa que se supere ser o Unix. No entanto, que algo nem pode ser compatível com POSIX; muitos dos legados Unixes congelaram o idioma implementado por /bin/sh e os utilitários no PATH padrão prior das mudanças exigidas pelo Unix95 (sim, Unix95, há vinte anos atrás e contando). Pode haver um conjunto de Unix95, ou mesmo POSIX.1-2001 se você tiver sorte, ferramentas em um diretório que not no PATH padrão (por exemplo, /usr/xpg4/bin ), mas eles não estão garantidos para existir.

No entanto, os conceitos básicos de Perl são more likely de estar presentes em uma instalação Unix arbitrariamente selecionada do que Bash é. (Por "o básico do Perl", eu quero dizer /usr/bin/perl existe e é some , possivelmente bastante antiga, versão do Perl 5, e se você tiver sorte, o conjunto de módulos que acompanha essa versão do intérprete também é acessível.)

Assim sendo:

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

Mas se você not está escrevendo algo que tem que trabalhar em todos os lugares, então, no momento em que você for tentado a usar qualquer Bashismo, você deve parar e reescrever tudo em uma linguagem de script melhor. Seu futuro será obrigado.

(Então, quando is apropriado usar as extensões Bash? Para a primeira ordem: nunca. Para segunda ordem: apenas para estender o ambiente interativo da Bash - por exemplo, para fornecer uma guia inteligente de guia e instruções extravagantes.)

Related questions

Hot questions

Language

Popular Tags