¿Hay un comando para ejecutar un script de acuerdo con su línea shebang?

46

Si quiero ejecutar un script bash que no tiene establecido su permiso de ejecución, puedo hacer lo siguiente:

bash script.sh

¿Qué debo usar en lugar de bashsi el script no es ejecutable y no conozco al intérprete correcto? ¿Hay algún comando que busque el intérprete desde la línea shebang y ejecute el script con él?

Aivar
fuente
¿Qué pasa con bash?
steffen
@steffen, ¿cómo sabes que el archivo en cuestión es un script bash?
muru
@muru cita de la pregunta: "Si quiero ejecutar un script bash ..." Además (incluso si no es un script bash), si bash whateverfunciona, ¿por qué usar algo diferente? bash está disponible en prácticamente todos los sistemas * ix, así que ¿por qué molestarse ...
steffen
1
@steffen, ¿leíste el resto de la pregunta? Dicen: "Si ... entonces puedo hacer: ..." y "¿Qué debo usar en lugar de bash si ... no conozco al intérprete correcto ?"
muru
@muru: tal vez no veo lo obvio. Pero si el archivo / has / a shebang line, como se indica en la pregunta, bash hará exactamente lo que se le pide. Como lo hará perl, de acuerdo con la respuesta a continuación. Entonces, ¿cuál es la ventaja de no usar bash?
steffen

Respuestas:

67

Sí. Se llama 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)

Esto se menciona en la documentación de Perl :

Si la #!línea no contiene la palabra "perl" ni la palabra "indir", el programa nombrado después de que #!se ejecuta en lugar del intérprete de Perl. Esto es un poco extraño, pero ayuda a las personas en máquinas que no lo hacen #!, porque pueden decirle a un programa que su SHELL es / usr / bin / perl, y Perl enviará el programa al intérprete correcto para ellos.

Ole Tange
fuente
40
Bueno, eso está sucio.
delgado
1
¿La extensión de archivo .jstambién funciona?
Pysis
1
Además, qué máquinas no hacen #!. Parece que tengo un poco más ahora y no he experimentado este problema.
Pysis
1
Ok, es solo que su ejemplo parecía resaltar muchas extensiones de archivo, en lugar de mostrar varias líneas shebang de archivos, y supongo que supuse que así era como funcionaba su predicción.
Pysis
2
Me gusta lo man perlruntímidamente que admite que es "un poco extraño" :). Creo que esto debería tratarse como una curiosidad dirigida a entornos que no son UNIX y versiones muy muy antiguas de UNIX.
delgado
25

Los guiones no necesariamente tienen un shebang

Si el script se ejecutó desde el intérprete, no puede estar seguro de que tenga el shebang en absoluto . Las secuencias de comandos, ejecutadas desde el intérprete no necesitan el shebang , si llama al intérprete para ejecutar el código.

Por lo tanto, la respuesta es no, no hay ningún comando que descubra con seguridad cuál es el idioma (intérprete) con el que ejecutar el script. Sin embargo, siempre puede mirar dentro del script y ver si tiene el shebang para descubrirlo.

Las reglas en resumen:

  1. Cuando ejecuta el script, llamar al intérprete siempre anula posibles shebangs, ejecutables o no, shebang o no.
  2. Si no es ejecutable y se ejecuta desde el intérprete, el script no necesita shebang.
  3. Si el script se ejecuta sin llamar primero al intérprete, necesita (y usa) el shebang para averiguar a qué intérprete llamar, y debe ser ejecutable para tener el "permiso" para llamar al intérprete desde su shebang.

Sin embargo, si el script no tiene shebang, no hay información (directa *) dentro del script para indicar qué intérprete usar.

Una vez dicho esto

Por supuesto, siempre puede escribir una secuencia de comandos de envoltura para intentar averiguar si la secuencia de comandos tiene el shebang y leer el intérprete a partir de eso, posteriormente ejecutarlo desde el intérprete encontrado.

Un ejemplo

#!/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")
  • Guárdelo como tryrunen $PATH(p ~/bin. Ej. , Cree el directorio si no existe, cierre sesión y vuelva a iniciarlo), hágalo ejecutable . Luego corriendo:

    tryrun /path/to/nonexecutablescript

    llama (probado) al intérprete correcto en mi no ejecutable pythony bashscripts.

Explicación

  • El guión simplemente lee la primera línea del guión, elimina el #!y usa el resto para llamar al intérprete.
  • Si no puede llamar a un intérprete válido, elevará a PermissionErroro a FileNotFoundError.

Nota

La extensión ( .sh, .pyetc.) no juega ningún papel en la determinación del intérprete apropiado en Linux.


(* Por supuesto, es posible desarrollar un algoritmo de conjetura "inteligente" para determinar la sintaxis del código).

Jacob Vlijm
fuente
Bien, significa que aunque Linux tiene implementada la extracción shebang en algún lugar (para que pueda seleccionar el intérprete correcto para los srcipts ejecutables), no se proporciona como un comando estándar independiente.
Aivar
@Aivar extraer el shebang no es el problema, pero ejecutar código sin él es perfectamente posible.
Jacob Vlijm
@Aivar Ah, veo a qué te refieres. Si el script es ejecutable y se ejecuta sin lenguaje en el comando, el script llama al intérprete, no al revés.
Jacob Vlijm
1
@JacobVlijm Yo no diría "el script llama al intérprete", más como "el kernel de Linux toma la línea shebang para averiguar a qué intérprete llamar al ejecutar el script".
Paŭlo Ebermann
@ PaŭloEbermann Gracias! Cierto por supuesto. El núcleo se encarga de todo el procedimiento de cualquier manera, pero en sentido figurado, y creo que para entenderlo mejor, es decir que el guión está "autorizado" a encargarse de qué intérprete llamar (y realmente hacerlo). No estoy seguro acerca de la redacción, pero me gustaría describirla como si la iniciativa estuviera en el script, mientras que el núcleo realmente hace el trabajo.
Jacob Vlijm
6

Puede lograr esto con un script como este:

#!/bin/bash

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

Así:

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

Recomiendo no hacer esto. Los permisos están ahí por una razón. Este es un programa para subvertir permisos.

Tenga en cuenta que el manejo de shebang es una función del núcleo (en el código fuente de Linux - fs/binfmt_script.c). Básicamente, el proceso que invoca un script directamente no conoce el #!- el núcleo lo usa para determinar que necesita lanzar un intérprete.

Delgado
fuente
2
Siempre supuse que era una función del shell, NO del núcleo. Aprendí algo nuevo hoy.
Boatcoder
1
@boatcoder: como está interesado, agregó un enlace a donde lo maneja el código fuente de Linux.
delgado