Por que o primeiro comentário de linha do arquivo .sh é necessário? [duplicado]

2

Foi-me dito que a primeira linha shebang ( #!/bin/bash ) é necessária no arquivo de script do shell, e o arquivo não será executado com precisão sem essa linha. Mas eu testei alguns scripts. Eles funcionam muito bem sem essa linha.

Essa linha é simplesmente um comentário? O bash omite essa linha, como acontece com outros comentários? Ou o bash interpreta isso?

Essa linha é forçada nesse formato? Ou apenas uma nota legível útil? E se eu colocar de outra maneira, digamos, sem o ! ?

    
por user43312 24.07.2013 / 01:04

5 respostas

8

A shebang informa ao kernel qual programa invocar quando o script é executado diretamente .

chmod +x myscript.sh
./myscript.sh
    
por 24.07.2013 / 01:10
8

O próprio kernel é bem claro sobre isso. O shebang deve estar lá, senão não considera tal arquivo um script executável.

de linux/fs/binfmt_script.c

static int load_script(struct linux_binprm *bprm)
{
[...]
    if ((bprm->buf[0] != '#') || (bprm->buf[1] != '!'))
            return -ENOEXEC;

No entanto, se o shebang estiver faltando, o shell pode optar por executar o arquivo em si. Por isso, parece funcionar para scripts de shell, mas não é um recurso em que você deve confiar.

No entanto, e se o seu script de shell usa bashisms? Você deve ter o #!/bin/bash então ele será executado no bash e não em algum outro shell. Além disso, ele será executado se o programa tentando executá-lo não for um shell em si. Depois, há scripts em linguagens completamente diferentes, como Perl ou Python. Sem o shebang, o sistema não saberá qual intérprete usar para eles.

    
por 24.07.2013 / 01:16
3

A shebang só é necessária se uma dessas condições for verdadeira:

  • seu script não é portátil, isto é, usa algo não suportado pelo padrão POSIX (por exemplo: bashisms, kshisms ou qualquer outro), portanto, é necessário especificar qual interpretador deve ser iniciado. Isso obviamente inclui linguagens de script alienígenas (csh, perl, python, ...). Como sua pergunta é sobre #!/bin/bash , provavelmente você está nesse caso.

  • você está executando seu script em um ambiente / modo não POSIX.

Caso contrário, o shebang não é obrigatório e, na verdade, nem é recomendado para scripts shell estritamente conformes. A razão é que apesar da crença popular, /bin/sh não é necessariamente o caminho padrão para um shell POSIX. A única maneira suportada de recuperar o local padrão do shell é executar

PATH='getconf PATH' command -v sh

Os estados padrão POSIX :

If the first line of a file of shell commands starts with the characters "#!", the results are unspecified.

    
por 24.07.2013 / 02:21
3

Se a primeira linha de um arquivo começar com #! , o sistema o tratará como um script cujo interpretador segue #! (e a maioria dos intérpretes ignora essa linha, pois # é o líder de comentário na maioria deles). Porém, a a sintaxe dessa linha de shebang e como ela é interpretada varia de sistema para sistema e não é especificada por POSIX (embora no caso de #! /bin/bash sozinho, não há ambigüidade).

Se você omitir a linha shebang em um arquivo de texto, quando executada por um utilitário compatível com POSIX, como env , xargs , find -exec , awk system() , vi , sh ... ou funções da biblioteca C em conformidade com POSIX como execl() , execp() , system() , popen() ... em um ambiente POSIX, o arquivo será interpretado em conformidade com POSIX sh .

Isso é o que o POSIX especifica e o mesmo vale para o Unix ao invés do POSIX.

Agora, isso deixa alguma incerteza sobre o que acontece quando as condições descritas acima não são significadas.

Por exemplo, em todos os sistemas, a chamada do sistema execve() falhará na execução desses scripts sem um shebang. Não é o kernel que implementa a lógica de iniciar um POSIX sh , nesse caso, são aplicativos / funções do espaço do usuário sobre execve , na verdade retornando ENOEXEC na tentativa de execução do arquivo.

O que isto significa é que se uma aplicação (tipicamente não especificada por POSIX) não depende de POSIX execl() , system() ... para executar um comando mas chamar execve() diretamente, eles falharão em execute esses scripts. Raro, mas acontece.

Agora há a questão do ambiente POSIX e quais aplicações shell não-POSIX chamarão.

POSIX permite que os sistemas não estejam em conformidade com base no ambiente, eles até permitem que o ambiente POSIX não seja o padrão.

Por exemplo, o Solaris antes do Solaris 11 tinha várias implementações de sh (e outros utilitários padrão), um padrão em /usr/xpg4/bin ou /usr/xpg6/bin e um histórico (não padrão) um em /bin . O $PATH padrão tinha /bin antes de /usr/xpg4/bin . Além disso, o que as coisas do shell como system() chamado dependiam de como o aplicativo que as utilizava era compilado (com o padrão sendo o shell não-POSIX /bin/sh ). Até mesmo os utilitários POSIX (como IIRC /usr/xpg4/bin/awk ) falharam em iniciar o shell apropriado em algumas circunstâncias (como em print | "some command" in awk IIRC).

O que isso significa é que no Solaris, ao omitir o shebang, você nunca poderia ter certeza se o script seria interpretado por um padrão sh ou pelo antigo shell Bourne em /bin .

Assim, para portabilidade, mesmo que POSIX implique que você deve não usar um shebang se quiser que seu script seja interpretado por um POSIX sh , na prática, você geralmente precisa e precisa se adaptar o caminho dependendo do sistema (como #! /usr/xpg4/bin/sh - no Solaris e #! /bin/sh - na maioria dos outros sistemas).

Agora, se você quiser que seu script seja interpretado por bash (por exemplo, porque ele usa bash extensões sobre a sintaxe sh padrão), então não há dúvida, estamos fora do que o POSIX especifica de qualquer maneira , e você precisa usar um shebang lá com o caminho correto para o interpretador bash no sistema que o script deve ser executado.

    
por 24.07.2013 / 03:55
1

Como complemento à resposta do frostschutz , algumas camadas têm a capacidade de interpretar linhas hash bang (shebang). Normalmente, o Bash ativa apenas esse recurso se o kernel não suportar a interpretação de hash bang.

No entanto, se você quiser jogar (hack) com suas linhas de shebangs, pode ser mais fácil brincar com elas na fonte do Bash do que na fonte do kernel.

De bash/execute_cmd.c :

#if !defined (HAVE_HASH_BANG_EXEC)
    if (sample_len > 2 && sample[0] == '#' && sample[1] == '!')
        return (execute_shell_script (sample, sample_len, command, args, env));
    else
#endif

De bash/config.h :

/* Define if the kernel can exec files beginning with #! */
#define HAVE_HASH_BANG_EXEC 1
    
por 24.07.2013 / 01:40