Muitas linhas de shebang (declaração de script) - alguma maneira de reduzir a quantidade delas?

11

Eu tenho um projeto composto por cerca de 20 pequenos arquivos .sh . Eu nomeio estes "pequenos" porque geralmente, nenhum arquivo tem mais de 20 linhas de código. Adotei uma abordagem modular porque, portanto, sou fiel à filosofia Unix e é mais fácil para mim manter o projeto.

No início de cada arquivo .sh , eu coloco #!/bin/bash .

Simplificando, eu entendo que declarações de script têm dois propósitos:

  1. Eles ajudam o usuário a recuperar qual shell é necessário para executar o arquivo (digamos, depois de alguns anos sem usar o arquivo).
  2. Eles garantem que o script seja executado apenas com um determinado shell (Bash, nesse caso) para evitar um comportamento inesperado no caso de outro shell ser usado.

Quando um projeto começa a crescer de 5 para 20 arquivos ou de 20 para 50 (não neste caso, mas apenas para demonstrar), temos 20 linhas ou 50 linhas de declarações de script. Eu admito, mesmo que possa ser engraçado para alguns, parece um pouco redundante para mim usar 20 ou 50, em vez dizer apenas 1 por projeto (talvez no arquivo principal do projeto).

Existe uma maneira de evitar essa suposta redundância de 20 ou 50 ou um número muito maior de linhas de declarações de script usando alguma declaração de script "global", em algum arquivo principal?

    
por user9303970 03.02.2018 / 13:49

6 respostas

19

Embora o seu projeto possa agora consistir apenas de 50 scripts Bash, mais cedo ou mais tarde ele começará a acumular scripts escritos em outras linguagens, como Perl ou Python (para os benefícios que essas linguagens de script têm que o Bash não possui).

Sem uma linha #! adequada em cada script, seria extremamente difícil usar os vários scripts sem também saber qual interpretador usar. Não importa se cada script é executado de outros scripts , isso apenas move a dificuldade dos usuários finais para os desenvolvedores. Nenhum desses dois grupos de pessoas precisa saber em que idioma um script foi escrito para poder usá-lo.

Shell scripts executados sem uma #! -line e sem um interpretador explícito são executados de diferentes maneiras, dependendo do shell que os invoca (ver, por exemplo, a pergunta Qual interpretador de shell executa um script sem shebang? e especialmente A resposta de Stéphane ), que não é o que você quer em um ambiente de produção (você quer um comportamento consistente e possivelmente até portabilidade).

Os scripts executados com um interpretador explícito serão executados por esse interpretador, independentemente do que a linha #! diz. Isso causará problemas mais adiante se você decidir reimplementar, digamos, um script Bash em Python ou qualquer outro idioma.

Você deve gastar essas teclas extras e sempre adicionar uma #! -line a cada script.

Em alguns ambientes, existem textos jurídicos com vários parágrafos em cada script em cada projeto. Fique muito feliz por ser apenas uma #! -line que parece "redundante" em seu projeto.

    
por 03.02.2018 / 14:26
11

Você entendeu mal o ponto de #! . Na verdade, é uma diretiva para o sistema operacional para executar este programa sob o interpretador definido.

Portanto, um script pode ser #!/usr/bin/perl e o programa resultante pode ser executado como ./myprogram e o intérprete perl pode ser usado. #!/usr/bin/python semelhante faria com que um programa fosse executado sob python.

Portanto, a linha #!/bin/bash diz ao sistema operacional para executar este programa no bash.

Veja um exemplo:

$ echo $0
/bin/ksh

$ cat x
#!/bin/bash

ps -aux | grep $$

$ ./x
sweh      2148  0.0  0.0   9516  1112 pts/5    S+   07:58   0:00 /bin/bash ./x
sweh      2150  0.0  0.0   9048   668 pts/5    S+   07:58   0:00 grep 2148

Então, apesar do meu shell ser o ksh, o programa "x" é executado sob bash por causa da linha #! , e podemos ver isso explicitamente na lista de processos.

    
por 03.02.2018 / 13:59
9

em .sh

O que é ruim é o .sh no final do nome do arquivo.

Imagine que você reescreve um dos scripts em python.

Bem, agora você tem que mudar a primeira linha para #!/usr/bin/python3 isto não é tão ruim, já que você teve que mudar todas as outras linhas de código no arquivo também. No entanto, você também precisa alterar o nome do arquivo de prog.sh para prog.py . E então você tem que encontrar em todos os lugares nos outros scripts, e todos os seus scripts de usuário (aqueles que você nem sabia que existiam), e alterá-los para usar o novo script. sed -e 's/.sh/.py/g' pode ajudar os que você conhece. Mas agora sua ferramenta de controle de revisão está mostrando uma alteração em muitos arquivos não relacionados.

Alternativamente, use a maneira Unix e nomeie o programa prog not prog.sh .

em #!

Se você tiver o #! correto e defina a permissão / modo para incluir a execução. Então você não precisa conhecer o intérprete. O computador fará isso por você.

chmod +x prog #on gnu adds execute permission to prog.
./prog #runs the program.

Para sistemas não-gnu, leia o manual para chmod (e aprenda octal), talvez leia de qualquer maneira.

    
por 03.02.2018 / 17:41
8

[The hashbang lines] ensure that the script runs only with a certain shell (Bash in that case) to prevent unexpected behavior in case another shell was used.

Provavelmente vale a pena ressaltar que isso é completamente errado. A linha de hashbang não impede que você tente alimentar o arquivo com um shell / interpretador incorreto:

$ cat array.sh
#!/bin/bash
a=(a b c)
echo ${a[1]} $BASH_VERSION
$ dash array.sh
array.sh: 2: array.sh: Syntax error: "(" unexpected

(ou similarmente com awk -f array.sh etc.)

Mas, em qualquer caso, outra abordagem à modularidade seria definir as funções do shell nos arquivos, uma função por arquivo, se desejar, e, em seguida, source os arquivos do programa principal. Dessa forma, você não precisaria de hashbangs: eles seriam tratados como qualquer outro comentário, e tudo seria executado usando o mesmo shell. A desvantagem seria que você precisaria source dos arquivos com as funções (por exemplo, for x in functions/*.src ; do source "$x" ; done ), e todos eles rodariam usando o mesmo shell, então você não poderia substituir diretamente um deles por um Perl- implementação.

    
por 03.02.2018 / 16:33
4

Is there a way to avoid this alleged redundancy of 20 or 50 or a much greater number of lines of script declarations by using some "global" script declaration, in some main file?

Existe - é chamado de portabilidade ou conformidade com POSIX. Esforce-se para sempre escrever scripts de maneira neutra, use-os apenas a partir de um script que usa #!/bin/sh ou quando você usa /bin/sh interativamente (ou pelo menos um shell compatível com POSIX como ksh ).

No entanto, os scripts portáteis não salvam você:

  • é necessário usar recursos de um dos shells ( a resposta do ilkkachu é um exemplo)
  • precisando usar linguagens de script mais avançadas que shells (Python e Perl)
  • Erro PEBKAC: seu script cai nas mãos do usuário J.Doe que executa seu script não como você pretendia, por exemplo, eles executam /bin/sh script com csh .

Se eu puder expressar minha opinião, "evitar a redundância" eliminando #! é a meta errada. O objetivo deve ser o desempenho consistente e na verdade um script em funcionamento que funcione e considere casos especiais, segue certas regras gerais como não-analisando-ls ou sempre-citando-variáveis-a menos-que-precisar-word- divisão .

They help the user recall what shell is needed to execute the file (say, after some years without using the file).

Eles não são realmente para o usuário - eles são para o sistema operacional executar intérprete adequado. "Proper", neste caso, significa que o sistema operacional encontra o que está em shebang; se o código abaixo para shell errado - isso é culpa do autor. Quanto à memória do usuário, eu não acho que o "usuário" de programas como o Microsoft Word ou o Google Chrome precise saber em que programas de idiomas estão escritos; autores podem precisar.

Então, novamente, os scripts escritos portavelmente podem eliminar a necessidade de lembrar qual shell você usou originalmente, porque os scripts portáteis devem funcionar em qualquer shell compatível com POSIX.

They ensure that the script runs only with a certain shell (Bash in that case) to prevent unexpected behavior in case another shell was used.

Eles não. O que realmente impede o comportamento inesperado é escrever scripts portáteis.

    
por 03.02.2018 / 18:48
2

O motivo pelo qual você precisa colocar #!/bin/bash no topo de cada script é executá-lo como um processo separado.

Quando você executa um script como um novo processo, o SO vê que é um arquivo de texto (em oposição a um executável binário) e precisa saber qual interpretador deve ser executado. Se você não disser, o sistema operacional só pode adivinhar, usando sua configuração padrão (que um administrador pode alterar). Portanto, a linha #! (além de adicionar permissões executáveis, é claro) transforma um arquivo de texto simples em um executável que pode ser executado diretamente, com a certeza de que o sistema operacional sabe o que fazer com ele.

Se você quiser remover a linha #! , suas opções são:

  1. Execute o script diretamente (como você está fazendo agora) e espero que o interpretador padrão corresponda ao script - você provavelmente está seguro na maioria dos sistemas Linux, mas não portável para outros sistemas (por exemplo, Solaris), e pode ser alterado para qualquer coisa (eu poderia até mesmo configurá-lo para executar vim no arquivo!).

  2. Execute o script usando bash myscript.sh . Isso funciona bem, mas você precisa especificar o caminho para o script e está confuso.

  3. Origem do script usando . myscript.sh , que executa o script no processo atual do intérprete (ou seja, não como um subprocesso). Mas isso quase certamente exigirá modificações em seus scripts e os tornará menos modulares / reutilizáveis.

Nos casos 2 e 3, o arquivo não precisa (e não deveria ter) permissões de execução, e dar a ele um sufixo .sh (ou .bash ) é uma boa ideia.

Mas nenhuma dessas opções parece ser boa para o seu projeto, já que você disse que deseja manter seus scripts modulares (= > independentes e autocontidos), então você deve manter sua linha #!/bin/bash no topo dos scripts .

Na sua pergunta, você também disse que está seguindo a filosofia Unix. Se isso for verdade, então você deve aprender para que serve a linha #! e continuar a usá-la em todos os scripts executáveis!

    
por 09.02.2018 / 02:48