Esiste un comando per eseguire uno script in base alla sua riga shebang?


46

Se voglio eseguire uno script bash che non ha il permesso di esecuzione impostato, posso fare:

bash script.sh

Cosa devo usare invece che bashse lo script non è eseguibile e non conosco l'interprete corretto? Esiste un comando che cerca l'interprete dalla riga shebang ed esegue lo script con esso?


Cosa c'è che non va con bash?
Steffen,

@steffen come fai a sapere se il file in questione è uno script bash?
muru,

@muru quote from Question: "Se voglio eseguire uno script bash ..." Inoltre (anche se non è uno script bash), se bash whateverfunziona, perché usare qualcosa di diverso? bash è disponibile praticamente su ogni sistema * ix, quindi perché preoccuparsi ...
steffen,

1
@steffen hai letto il resto della domanda? Dicono: "Se ... allora posso fare: ..." e "Cosa dovrei usare al posto di bash se ... Non conosco l'interprete corretto ?"
Muru,

@muru: forse non vedo l'ovvio. Ma se il file / has / a shebang line, come indicato nella domanda, bash farà esattamente ciò che viene richiesto. Come perl, secondo la risposta di seguito. Quindi qual è il vantaggio di non usare bash?
Steffen,

Risposte:


67

Sì. Si chiama 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)

Questo è menzionato nella documentazione di Perl :

Se la #!riga non contiene la parola "perl" né la parola "indir", il programma che prende il nome #!viene eseguito al posto dell'interprete Perl. Questo è leggermente bizzarro, ma aiuta le persone su macchine che non lo fanno #!, perché possono dire a un programma che il loro SHELL è / usr / bin / perl, e Perl invierà quindi il programma all'interprete corretto per loro.


40
Bene, è solo sporco.
magro,

1
Funziona anche l'estensione del file .js?
Pysis,

1
Inoltre, quali macchine non fanno #!. Mi sembrano pochi o più adesso e non ho riscontrato questo problema.
Pysis,

1
Ok, è solo il tuo esempio che sembra evidenziare molte estensioni di file, piuttosto che mostrare diverse linee di shebang dai file, e immagino di aver supposto che funzionasse la sua previsione.
Pysis,

2
Mi piace il modo in cui man perlrunammette timidamente che è "leggermente bizzarro" :). Penso che questo dovrebbe essere trattato come una curiosità rivolta ad ambienti non UNIX e versioni molto vecchie di UNIX.
magro

25

Gli script non hanno necessariamente un shebang

Se lo script è stato eseguito dall'interprete, non puoi essere sicuro che ha la baracca a tutti . Gli script eseguiti dall'interprete non necessitano dello shebang , se si chiama l'interprete per eseguire il codice.

La risposta è quindi no, non esiste alcun comando che scoprirà con certezza qual è la lingua (interprete) con cui eseguire lo script. Puoi comunque sempre guardare all'interno dello script e vedere se ha lo shebang per scoprirlo.

Le regole in breve:

  1. Quando si esegue lo script, la chiamata all'interprete esclude sempre eventuali shebang, eseguibili o meno, shebang o no.
  2. Se non è eseguibile e lanciarlo dal l'interprete, lo script non ha bisogno di baracca.
  3. Se lo script viene eseguito senza prima chiamare l'interprete, ha bisogno (e usa) lo shebang per scoprire quale interprete chiamare e deve essere eseguibile per avere il "permesso" di chiamare l'interprete dal suo shebang.

Se lo script non ha alcun shebang, non ci sono informazioni (dirette *) all'interno dello script per dire quale interprete usare.

Avendolo detto

Naturalmente puoi sempre scrivere uno script wrapper per provare a scoprire se lo script ha lo shebang e leggere l'interprete da quello, successivamente eseguirlo dall'interprete trovato.

Un esempio

#!/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")
  • Salvarlo come tryrunin $PATH(ad esempio ~/bin, rendere la directory se non esiste, disconnettersi e riconnettersi), renderla eseguibile . Quindi in esecuzione:

    tryrun /path/to/nonexecutablescript

    chiama (testato) l'interprete corretto sul mio non eseguibile pythone sugli bashscript.

Spiegazione

  • Lo script legge semplicemente la prima riga dello script, rimuove #!e usa il resto per chiamare l'interprete.
  • Se non riesce a chiamare un interprete valido, genererà a PermissionErroro a FileNotFoundError.

Nota

L'estensione ( .sh, .pyecc.) Non svolge alcun ruolo nel determinare l'interprete appropriato su Linux.


(* È ovviamente possibile sviluppare un algoritmo di ipotesi "intelligente" per determinare la sintassi dal codice.)


OK, quindi significa che sebbene Linux abbia implementato l'estrazione shebang da qualche parte (in modo che possa selezionare l'interprete corretto per gli eseguibili), non è fornito come comando standard autonomo.
Aivar,

@Aivar l'estrazione dello shebang non è un problema, ma eseguire il codice senza di esso è perfettamente possibile.
Jacob Vlijm,

@Aivar Ah, capisco cosa intendi. Se lo script è eseguibile ed eseguito senza linguaggio nel comando, lo script chiama l'interprete, non viceversa.
Jacob Vlijm,

1
@JacobVlijm Non direi "lo script chiama l'interprete", più come "il kernel Linux prende la linea shebang per capire quale interprete chiamare quando si esegue lo script".
Paŭlo Ebermann,

@ PaŭloEbermann Grazie! Vero ovviamente. Il kernel si occupa dell'intera procedura in entrambi i modi, ma in senso figurato, e penso meglio per capire, è dire che lo script è "autorizzato" a prendersi cura di quale interprete chiamare (e effettivamente farlo). Non sono sicuro della formulazione, ma mi piacerebbe descriverlo come se l'iniziativa fosse sullo script, mentre il kernel fa davvero il lavoro.
Jacob Vlijm,

6

Puoi farlo con uno script come questo:

#!/bin/bash

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

Così:

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

Consiglio di non farlo. Le autorizzazioni sono lì per un motivo. Questo è un programma per sovvertire le autorizzazioni.

Si noti che la gestione di shebang è una funzione del kernel (nel codice sorgente di Linux - fs/binfmt_script.c). Fondamentalmente il processo che invoca direttamente uno script non è a conoscenza del #!- il kernel lo usa per capire che deve avviare un interprete.


2
Avevo sempre pensato che fosse una funzione della shell, NON del kernel. Ho imparato qualcosa di nuovo oggi.
Boatcoder

1
@boatcoder - Dato che sei interessato, ha aggiunto un link a dove lo gestisce il codice sorgente Linux.
magro
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.