Por que esse script funciona no terminal, mas não a partir de um arquivo?

19

Eu tenho esse script de shell salvo em um arquivo: ele faz alguma substituição básica de string.

#!/bin/sh
html_file=$1
echo "html_file = $html_file"
substr=.pdf
pdf_file="${html_file/.html/.pdf}"
echo "pdf_file = $pdf_file"

Se eu colar na linha de comando, funcionará bem:

$ html_file="/home/max/for_pauld/test_no_base64.html"
  echo "html_file = $html_file"
  substr=.pdf
  pdf_file="${html_file/.html/.pdf}"
  echo "pdf_file = $pdf_file"

html_file = /home/max/for_pauld/test_no_base64.html
pdf_file = /home/max/for_pauld/test_no_base64.pdf

Essa é a saída do eco acima - está funcionando conforme o esperado.

Mas, quando eu chamo o script, com

$ saucer "/home/max/for_pauld/test_no_base64.html"

Eu recebo esta saída:

html_file = /home/max/for_pauld/test_no_base64.html
/home/max/bin/saucer: 5: /home/max/bin/saucer: Bad substitution

Meu script está usando uma versão diferente do bash ou algo assim? Preciso mudar minha linha shebang?

Max Williams
fonte
6
Essa expansão de parâmetro é um basismo (bem, não POSIX): mude sua aparência.
jasonwryan
Obrigado! Isso funcionou. Acho que estou um pouco confuso com a diferença entre she bash. Vou ler sobre isso. Se você se incomodou em fazer o seu comentário em uma resposta, marcarei como correto.
Max Williams
Deu uma nota ao The Duke por sua resposta detalhada, mas obrigado de qualquer maneira.
Max Williams
2
@ MaxWilliams: em poucas palavras, sh é a casca de bourne original. Todos os scripts compatíveis devem ser escritos para sh. Mas muitas conchas subseqüentes (por exemplo: bash, Bourne Again shell) são "quase compatíveis" e acrescentam muita gentileza extra. Como a substituição que você usou. Atualmente, você está bastante seguro usando apenas os recursos posix, mas lembre-se de que a portabilidade depende mesmo de um conjunto mais restrito (ou seja, apenas sh). Então, em geral, use: #!/usr/bin/env bashcomo seu shebang, e use substituições definidas como desejar, mas com ressalvas de portabilidade. E leia: unix.stackexchange.com/a/48787/27616
Olivier Dulac

Respostas:

37

O que é sh

sh(ou a Shell Command Language) é uma linguagem de programação descrita pelo padrão POSIX . Ele tem muitas implementações ( ksh88, dash...). bashtambém pode ser considerada uma implementação de sh(veja abaixo).

Porque shé uma especificação, não uma implementação, /bin/shé um link simbólico (ou um link físico) para uma implementação real na maioria dos sistemas POSIX.

O que é bash

bashcomeçou como uma shimplementação compatível (embora anteceda o padrão POSIX em alguns anos), mas com o passar do tempo, adquiriu muitas extensões. Muitas dessas extensões podem alterar o comportamento de scripts de shell POSIX válidos, portanto, por si só, bashnão é um shell POSIX válido. Pelo contrário, é um dialeto da linguagem shell POSIX.

bashsuporta um --posixswitch, o que o torna mais compatível com POSIX. Ele também tenta imitar o POSIX se chamado como sh.

sh = festança?

Por um longo tempo, /bin/shcostumava apontar para /bin/basha maioria dos sistemas GNU / Linux. Como resultado, quase se tornou seguro ignorar a diferença entre os dois. Mas isso começou a mudar recentemente.

Alguns exemplos populares de sistemas para os quais /bin/shnão aponta /bin/bash(e em alguns dos quais /bin/bashpodem até não existir) são:

  1. Sistemas modernos Debian e Ubuntu, que simbolizam shcomo dashpadrão;
  2. Busybox , que geralmente é executado durante o tempo de inicialização do sistema Linux como parte de initramfs. Ele usa a ashimplementação do shell.
  3. BSDs e, em geral, quaisquer sistemas não Linux. O OpenBSD usa pdksh, um descendente do shell Korn. O FreeBSD shé um descendente do shell original do UNIX Bourne. O Solaris possui um sistema shque, por um longo tempo, não era compatível com POSIX; uma implementação gratuita está disponível no projeto Heirloom .

Como você pode descobrir o que /bin/shaponta para o seu sistema?

A complicação é que /bin/shpoderia ser um link simbólico ou um link físico. Se for um link simbólico, uma maneira portátil de resolvê-lo é:

% file -h /bin/sh
/bin/sh: symbolic link to bash

Se for um link físico, tente

% find -L /bin -samefile /bin/sh
/bin/sh
/bin/bash

De fato, a -Lflag cobre links simbólicos e hardlinks, mas a desvantagem desse método é que ele não é portátil - o POSIX não precisa find oferecer suporte à -samefileopção, embora o GNU find e o FreeBSD o apóiem.

Linha Shebang

Por fim, cabe a você decidir qual usar, escrevendo a linha «shebang».

Por exemplo

#!/bin/sh

usará sh(e o que quer que isso aconteça),

#!/bin/bash

usará /bin/bashse estiver disponível (e falhará com uma mensagem de erro, se não estiver). Obviamente, você também pode especificar outra implementação, por exemplo

#!/bin/dash

Qual usar

Para meus próprios scripts, prefiro shpelos seguintes motivos:

  • é padronizado
  • é muito mais simples e fácil de aprender
  • ele é portátil nos sistemas POSIX - mesmo que eles não tenham bash, eles precisam tersh

Existem vantagens em usar bashtambém. Seus recursos tornam a programação mais conveniente e semelhante à programação em outras linguagens de programação modernas. Isso inclui coisas como variáveis ​​locais e matrizes com escopo. Plain shé uma linguagem de programação muito minimalista.

Hunter.S. Thompson
fonte
Obrigado pela resposta detalhada: Eu uso o Linux Mint, que é baseado no Debian e /bin/shé realmente um link simbólico para /bin/dash.
Max Williams
Se você deseja aderir ao POSIX sh, é melhor deixar de fora completamente: se a primeira linha de um arquivo de comandos shell começar com os caracteres "#!", Os resultados serão pubs.opengroup.org/onlinepubs/009695399/ utilitários /…
Daniel Jour
4
@DanielJour Se algum sistema semelhante ao Unix deixasse o suporte habitual para shebangs, ele quebraria tantas coisas que seria praticamente inutilizável. Portanto, é um padrão de fato, mesmo que não esteja especificado no POSIX. O único problema de compatibilidade real é que o caminho para os intérpretes pode ser diferente.
Barmar 19/07/19
23

Acrescentando à excelente resposta de @ Hunter.S. Thompson, gostaria de salientar que a parte não portátil do script é

pdf_file="${html_file/.html/.pdf}"

O ${variable/search/replace}é uma extensão GNU. Mas você pode evitá-lo facilmente com o POSIX puro:

pdf_file="${html_file%.html}".pdf

Após Hunter, esta é a melhor solução do que mudar o shebang para #! /bin/bash

Philippos
fonte
Obrigado - eu concordo que é a correção "mais pura", mas eu prefiro o shebang carregado bash, pois bash é o que eu costumo usar no terminal (por padrão) e é mais fácil fazer com que os scripts façam a mesma coisa que o normal linha de comando.
Max Williams
11
@MaxWilliams Claro, não há problema. Isso foi principalmente direcionado para o banco de dados de perguntas e respostas.
Philippos