Existe um comando para executar um script de acordo com sua linha shebang?

46

Se eu quiser executar um script bash que não tenha sua permissão de execução definida, eu posso fazer:

bash script.sh

O que devo usar em vez de bashse o script não é executável e não conheço o intérprete correto? Existe um comando que procura o intérprete na linha shebang e executa o script com ele?

Aivar
fonte
O que há de errado com bash?
Steffen
@ steffen como você sabe que o arquivo em questão é um script bash?
Muru
@ citação de muru da pergunta: "Se eu quiser executar um script bash ..." Além disso (mesmo que não seja um script bash), se bash whateverfuncionar, por que usar algo diferente? bash é acessível em praticamente todos os sistemas ix *, então porque se preocupar ...
Steffen
1
@steffen você leu o resto da pergunta? Eles dizem: "Se ... então eu posso fazer: ..." e "O que devo usar em vez do bash se ... não conheço o intérprete correto ?"
Muru
@ muru: talvez eu não veja o óbvio. Mas se o arquivo / has / uma linha shebang, conforme declarado na pergunta, o bash fará exatamente o que for solicitado. Como será perl, de acordo com a resposta abaixo. Então, qual é a vantagem de não usar o bash?
Steffen

Respostas:

67

Sim. É chamado perl:

perl foo.bash    # works
perl foo.lua     # works
perl foo.clisp   # works
perl foo.csh     # works
perl foo.php     # works
perl foo.gnuplot # works (no arguments)
perl foo.pl      # works (obviously)
perl foo.py      # works
perl foo.sh      # works
perl foo.tcl     # works
perl foo.rb      # works
perl foo.nodejs  # works
perl foo.r       # works
perl foo.oct     # works
perl foo.csharp  # works (no arguments)

Isso é mencionado na documentação do Perl :

Se a #!linha não contiver a palavra "perl" nem a palavra "indir", o programa nomeado após o #!é executado em vez do interpretador Perl. Isso é um pouco bizarro, mas ajuda as pessoas em máquinas que não o fazem #!, porque elas podem dizer a um programa que seu SHELL é / usr / bin / perl, e o Perl enviará o programa ao intérprete correto para elas.

Ole Tange
fonte
40
Bem, isso é apenas sujo.
magro
1
A extensão do arquivo .jstambém funciona?
Pysis
1
Além disso, o que as máquinas não fazem #!. Eu pareço um pouco mais agora e não tive esse problema.
Pysis
1
Ok, é apenas o seu exemplo que parecia destacar muitas extensões de arquivo, em vez de mostrar várias linhas shebang de arquivos, e acho que presumi que era assim que a previsão funcionava.
Pysis
2
Eu gosto de como man perlruntimidamente admite que é "um pouco bizarro" :). Eu acho que isso deve ser tratado como uma curiosidade voltada para ambientes não-UNIX e versões muito muito antigas do UNIX.
magro
25

Os scripts não têm necessariamente um shebang

Se o script foi executado a partir do intérprete, Você não pode ter certeza de que tem o shebang em tudo . Os scripts, executados no intérprete, não precisam do shebang , se você chamar o intérprete para executar o código.

A resposta é, portanto, não, não há nenhum comando que descubra com certeza qual é o idioma (intérprete) para executar o script. No entanto, você sempre pode olhar dentro do script e ver se ele tem o que descobrir.

As regras em resumo:

  1. Quando você executa o script, chamar o intérprete sempre anula possíveis shebangs, executáveis ​​ou não, shebang ou não.
  2. Se não for executável e executado a partir do intérprete, o script não precisa de shebang.
  3. Se o script for executado sem chamar o intérprete primeiro, ele precisa (e usa) o shebang para descobrir qual intérprete chamar e precisa ser executável para ter a "permissão" para chamar o intérprete a partir do shebang.

Se o script não tiver shebang, no entanto, não há informações (diretas *) dentro do script para dizer qual intérprete usar.

Tendo dito isto

É claro que você sempre pode escrever um script de invólucro para tentar descobrir se o script possui o shebang e ler o intérprete a partir dele e, posteriormente, executá-lo a partir do intérprete encontrado.

Um exemplo

#!/usr/bin/env python3
import subprocess
import sys

args = sys.argv[1:]; script = args[0]

try:
    lang = open(script).readlines()[0].replace("#!", "").strip().split()[-1]
    cmd = [lang, script]+args[1:]
    subprocess.call(cmd)
except (PermissionError, FileNotFoundError, IndexError):
    print("No valid shebang found")
  • Salve-o como tryrunem $PATH(por exemplo ~/bin, torne o diretório se ele não existir, efetue logout e logon novamente), torne-o executável . Em seguida, executando:

    tryrun /path/to/nonexecutablescript

    chamadas (testado) O intérprete correta no meu não-executável pythone bashscripts.

Explicação

  • O script simplesmente lê a primeira linha do script, remove #!e usa o restante para chamar o intérprete.
  • Se não conseguir chamar um intérprete válido, ele exibirá a PermissionErrorou a FileNotFoundError.

Nota

A extensão ( .sh, .pyetc) não desempenha nenhum qualquer papel na determinação do intérprete apropriado no Linux.


(* É claro que é possível desenvolver um algoritmo de adivinhação "inteligente" para determinar a sintaxe do código.)

Jacob Vlijm
fonte
OK, então isso significa que, embora o Linux tenha implementado a extração shebang em algum lugar (para que ele possa selecionar o intérprete correto para srcipts executáveis), ele não é fornecido como um comando padrão independente.
Aivar
@Aivar extrair o shebang não é o problema, mas executar o código sem ele é perfeitamente possível.
Jacob Vlijm
@Aivar Ah, entendo o que você quer dizer. Se o script é executável e executar sem linguagem no comando, o script chama o intérprete, e não o contrário.
Jacob Vlijm
1
@JacobVlijm Eu não diria "o script chama o intérprete", mais como "o kernel do Linux usa a linha shebang para descobrir qual intérprete chamar ao executar o script".
Paŭlo Ebermann 17/11
@ PaŭloEbermann Obrigado! É verdade, é claro. O kernel cuida de todo o procedimento de qualquer maneira, mas, figurativamente, e acho melhor para entender, é dizer que o script é "permitido" para cuidar do que o intérprete deve chamar (e realmente o faz). Não tenho certeza sobre o texto, mas gostaria de descrevê-lo como se a iniciativa estivesse no script, enquanto o kernel realmente faz o trabalho.
23416 Jacob Vlijm
6

Você pode conseguir isso com um script como este:

#!/bin/bash

copy=/tmp/runner.$$
cp $1 ${copy}
chmod u+x ${copy}
${copy}
rm ${copy}

Portanto:

$ echo "echo hello" > myscript
$ ./myscript
bash: ./myscript: Permission denied
$ ./runscript myscript 
hello

Eu recomendo não fazer isso. As permissões existem por um motivo. Este é um programa para subverter permissões.

Observe que o manuseio do shebang é uma função do kernel (no código-fonte do Linux - fs/binfmt_script.c). Fundamentalmente, o processo que chama um script diretamente não sabe sobre o #!- o kernel o usa para descobrir que ele precisa iniciar um intérprete.

magro
fonte
2
Eu sempre assumi que isso era uma função do shell, NÃO do kernel. Aprendi algo novo hoje.
boatcoder
1
@ boatcoder - Como você está interessado, adicionou um link para onde o código fonte do Linux lida com isso.
magro