Eu costumo trabalhar com servidores Ubuntu LTS que pelo que entendi link simbólico /bin/sh
para /bin/dash
. Um monte de outras distros embora link simbólico /bin/sh
para /bin/bash
.
Pelo que entendi, 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 sobre qual shell usar para scripts quando se deseja portabilidade máxima desses scripts entre servidores?
#!/bin/sh
e não use nada além do que o shell original forneceu.Respostas:
Existem aproximadamente quatro níveis de portabilidade para scripts de shell (no que diz respeito à linha shebang):
Mais portáteis: use um
#!/bin/sh
shebang e use apenas 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 versões anteriores que possuíam o shell Bourne legado real, anterior ao POSIX de forma não compatível, como/bin/sh
.)Segundo mais portátil: use uma
#!/bin/bash
(ou#!/usr/bin/env bash
) linha shebang e atenha-se aos recursos do bash v3. Isso funcionará em qualquer sistema que tenha o bash (no local esperado).Terceiro mais portátil: use uma
#!/bin/bash
(ou#!/usr/bin/env bash
) linha shebang 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 licença).Menos portátil: use um
#!/bin/sh
shebang e use extensões bash na 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 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 máxima portabilidade, use a opção nº 1, mas, na minha experiência, alguns recursos do bash (como matrizes) são úteis o suficiente para que eu vá com a nº 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 as extensões do bash, use um shebang do bash (por exemplo, opção 2) ou teste o script completamente com um shell muito básico (como traço nos servidores Ubuntu LTS). O wiki do Ubuntu tem uma boa lista de bashismos a serem observados .
Há algumas informações muito boas sobre o histórico e as diferenças entre os shells na pergunta Unix e Linux "O que significa ser compatível com sh?" 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 diferentes sistemas; se você está acostumado ao linux, está acostumado aos comandos GNU, que possuem 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á uma regra simples aqui, você apenas precisa conhecer o intervalo de variação dos comandos que está usando.
Um dos comandos piores em termos de portabilidade é uma das mais básicas:
echo
. Sempre que você o usar com qualquer opção (por exemplo,echo -n
ouecho -e
), ou com qualquer escape (barra invertida) na string a ser impressa, versões diferentes farão coisas diferentes. Sempre que desejar imprimir uma sequência sem avanço de linha ou com escapes na sequência, use-oprintf
(e aprenda como funciona - é mais complicado do queecho
é). Ops
comando também é uma bagunça .Outra coisa geral a se observar são as extensões recentes / GNUish para comando da sintaxe da opção: formato de comando antigo (padrão) é que o comando é seguido por opções (com um único traço e cada opção com uma única letra), seguido por argumentos de comando. As variantes recentes (e geralmente não portáteis) incluem opções longas (geralmente introduzidas com
--
), permitindo que as opções sejam apresentadas após os argumentos e usando--
para separar as opções dos argumentos.fonte
sh
), portanto,/bin/sh
não é garantido que seja o caminho certo. O mais portátil é, então, não especificar nenhum shebang ou adaptar o shebang ao sistema operacional usado.#!/bin/sh
para#!/bin/bash
, o script funcionou perfeitamente. (Para minha defesa, que roteiro tinha evoluído ao longo do tempo a partir de um que realmente só precisava sh-ismos, para uma que contou com bash-como o comportamento.)ksh
comportamento, não do Bourne. O que a maioria das distribuições Linux após o FHS faz é ter/bin/sh
um link simbólico para o shell que elas selecionam para fornecer compatibilidade com POSIX, normalmentebash
oudash
.No
./configure
script que prepara a linguagem TXR para a construção, escrevi o seguinte prólogo para melhor portabilidade. O script será auto-inicializado, mesmo que#!/bin/sh
seja um Bourne Shell antigo não compatível com POSIX. (Eu construo todas as versões em uma Solaris 10 VM).A idéia aqui é encontrar um shell melhor do que o que estamos executando e executar novamente o script usando esse shell. A
txr_shell
variável de ambiente está definida, para que o script reexecutado saiba que é a instância recursiva reexecutada.(No meu script, a
txr_shell
variável também é usada posteriormente, com exatamente dois propósitos: em primeiro lugar, é impressa como parte de uma mensagem informativa na saída do script. Em segundo lugar, é instalada como aSHELL
variável noMakefile
, paramake
usar isso shell também para executar receitas.)Em um sistema onde
/bin/sh
está o traço, você pode ver que a lógica acima encontrará/bin/bash
e reexecutará o script com isso.Em uma caixa Solaris 10, a entrada
/usr/xpg4/bin/sh
será iniciada se nenhum Bash for encontrado.O prólogo é escrito em um dialeto conservador de shell, usando
test
para testes de existência de arquivo, e o${@+"$@"}
truque para expandir argumentos que atendem a alguns shells antigos quebrados (o que seria simplesmente"$@"
se estivéssemos em um shell em conformidade com POSIX).fonte
x
hackery se citações adequadas estivessem em uso, uma vez que as situações que exigiam esse idioma envolvem invocações de teste agora obsoletas-a
ou-o
combinação de vários testes.test x$whatever
que eu estou cometendo lá parece uma cebola no verniz. Se não podemos confiar no antigo shell quebrado para fazer a citação, a${@+"$@"}
tentativa final é inútil.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é Tcl (sem dúvida). Se você precisar fazer algo um pouco complicado, ficará mais feliz a longo prazo se usar um dos itens acima em vez de um shell script.
A única vantagem que a linguagem shell ainda tem sobre as linguagens mais recentes é que algo que se chama
/bin/sh
é garantido que existe em qualquer coisa que pretenda ser Unix. No entanto, esse item pode até não ser compatível com POSIX; muitos dos Unixes proprietários herdados congelaram o idioma implementado pelo/bin/sh
e pelos utilitários no PATH padrão antes das alterações exigidas pelo Unix95 (sim, Unix95, vinte anos atrás e contando). Pode haver um conjunto de ferramentas Unix95 ou POSIX.1-2001, se você tiver sorte, em um diretório que não esteja no PATH padrão (por exemplo/usr/xpg4/bin
), mas não há garantia de que elas existem.No entanto, é mais provável que o básico do Perl esteja presente em uma instalação Unix selecionada arbitrariamente do que o Bash. (Por "princípios básicos do Perl", quero dizer,
/usr/bin/perl
existe e é uma versão do Perl 5, possivelmente bastante antiga, e se você tiver sorte, o conjunto de módulos fornecidos com essa versão do interpretador também estará disponível.)Assim sendo:
Se você está escrevendo algo que tem que funcionar em qualquer lugar que pretenda ser Unix (como um script "configure"), você precisa usá
#! /bin/sh
-lo e não usar nenhuma extensão. Atualmente, eu escreveria um shell compatível com POSIX.1-2001 nessa circunstância, mas estaria preparado para corrigir os POSIXismos se alguém pedisse suporte para ferro enferrujado.Mas se você não estiver escrevendo algo que funcione em qualquer lugar, no momento em que for tentado a usar qualquer bashismo, pare e reescreva tudo em uma linguagem de script melhor. Seu futuro eu agradecerá.
(Então, quando é apropriado usar extensões Bash? Na primeira ordem: nunca. Na segunda ordem: apenas para estender o ambiente interativo da Bash - por exemplo, para fornecer guias inteligentes e solicitações sofisticadas).
fonte
find ... -print0 |while IFS="" read -rd "" file; do ...; done |tar c --null -T -
. Não seria possível fazê-lo funcionar corretamente sem bashisms (read -d ""
) e extensões GNU (find -print0
,tar --null
). Reimplementar essa linha no Perl seria muito mais longo e desajeitado. Esse é o tipo de situação em que usar bashisms e outras extensões não POSIX é a coisa certa a fazer.perl -MFile::Find -e 'our @tarcmd = ("tar", "c"); find(sub { return unless ...; ...; push @tarcmd, $File::Find::name }, "."); exec @tarcmd
Observe que não apenas você não precisa de basismos, como também não precisa de extensões tar do GNU. (Se o comprimento da linha de comando for uma preocupação ou você desejar que a verificação e o tar sejam executados em paralelo, será necessáriotar --null -T
).find
retornou mais de 50.000 nomes de arquivos no meu cenário, portanto é provável que seu script atinja o limite de tamanho do argumento. Uma implementação correta que não use extensões não-POSIX teria que criar a saída do tar incrementalmente, ou talvez usar Archive :: Tar :: Stream no código Perl. Obviamente, isso pode ser feito, mas, neste caso, o script Bash é muito mais rápido de escrever e tem menos clichê e, portanto, menos espaço para erros.find ... -print0 |perl -0ne '... print ...' |tar c --null -T -
. Mas as perguntas são: 1) Estou usandofind -print0
e detar --null
qualquer maneira, então qual é o objetivo de evitar o basismoread -d ""
, que é exatamente o mesmo tipo de coisa (uma extensão GNU para manipular separador nulo que, diferentemente do Perl, pode se tornar algo POSIX algum dia )? 2) Perl é realmente mais difundido que Bash? Eu tenho usado imagens do Docker recentemente, e muitas delas têm Bash, mas não Perl. É provável que novos scripts sejam executados lá, do que nos antigos Unices.