Czy istnieje polecenie uruchamiania skryptu zgodnie z linią shebang?

46

Jeśli chcę wykonać skrypt bash, który nie ma ustawionego uprawnienia do wykonywania, mogę:

bash script.sh

Czego powinienem użyć, bashjeśli skrypt nie jest wykonywalny i nie znam poprawnego interpretera? Czy istnieje polecenie, które wyszukuje interpreter z linii shebang i wykonuje za jego pomocą skrypt?

Aivar
źródło
Co jest nie tak?
steffen,
@steffen skąd wiesz, że dany plik to skrypt bash?
muru
@muru cytat z pytania: „Jeśli chcę wykonać skrypt bash ...” Co więcej (nawet jeśli nie jest to skrypt bash), jeśli bash whateverdziała, po co używać czegoś innego? bash jest dostępny na praktycznie każdym * systemie IX, więc po co zawracać sobie
głowę
1
@steffen czy przeczytałeś resztę pytania? Mówią: „Jeśli ... to mogę zrobić: ...” i „Czego powinienem użyć zamiast bashu, jeśli ... Nie znam poprawnego tłumacza ?”
mur
@muru: może nie widzę rzeczy oczywistych. Ale jeśli linia pliku / has / shebang, jak podano w pytaniu, bash zrobi dokładnie to, o co zostanie poproszony. Jak będzie perl, zgodnie z odpowiedzią poniżej. Jaka jest więc zaleta nie używania bash?
steffen

Odpowiedzi:

67

Tak. Nazywa się to 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)

Jest to wspomniane w dokumentacji Perla :

Jeśli #!wiersz nie zawiera słowa „perl” ani słowa „indir”, #!zamiast interpretera Perla wykonywany jest program nazwany po nim . Jest to nieco dziwne, ale pomaga ludziom na maszynach, które tego nie robią #!, ponieważ mogą powiedzieć programowi, że ich SHELL to / usr / bin / perl, a następnie Perl wyśle ​​program do odpowiedniego interpretera dla nich.

Ole Tange
źródło
40
Cóż, to po prostu brudne.
szczupły,
1
Czy rozszerzenie pliku .jsrównież działa?
Pysis
1
Ponadto, czego nie robią maszyny #!. Wydaje mi się, że teraz jest ich kilka lub więcej i nie spotkałem się z tym problemem.
Pysis
1
Ok, to tylko twój przykład wydaje się podkreślać wiele rozszerzeń plików, zamiast pokazywać kilka linii shebang z plików, i przypuszczam, że tak właśnie działało jego przewidywanie.
Pysis,
2
Podoba mi się, jak man perlrunnieśmiało przyznaje, że to „trochę dziwne” :). Myślę, że należy to potraktować jako ciekawość skierowaną do środowisk innych niż UNIX i bardzo bardzo starych wersji UNIX.
szczupły,
25

Skrypty niekoniecznie mają shebang

Jeśli skrypt był uruchamiany z tłumacza, nie możesz być pewien, że ma shebang w ogóle . Skrypty uruchamiane z interpretera nie wymagają shebang , jeśli wywołasz interpretera w celu uruchomienia kodu.

Odpowiedź brzmi więc nie, nie ma polecenia, które na pewno dowie się, w jakim języku (tłumaczu) uruchamia się skrypt. Zawsze możesz jednak zajrzeć do skryptu i sprawdzić, czy zawiera on shebang.

Zasady w skrócie:

  1. Po uruchomieniu skryptu wywołanie interpretera zawsze unieważnia możliwe sekwencje, pliki wykonywalne lub nie, sekwencje lub nie.
  2. Jeśli skrypt nie jest wykonywalny i uruchamiany z interpretera, skrypt nie wymaga shebang.
  3. Jeśli skrypt jest uruchamiany bez uprzedniego wywołania interpretera, potrzebuje (i używa) shebang, aby dowiedzieć się, do którego tłumacza ma zadzwonić, i musi być wykonywalny, aby mieć „pozwolenie” na wywołanie interpretera z jego shebang.

Jeśli jednak skrypt nie ma shebang, w skrypcie nie ma żadnych informacji (bezpośrednich *), które wskazywałyby, jakiego interpretera użyć.

To powiedziawszy

Można oczywiście zawsze napisać skrypt otoki, aby spróbować dowiedzieć się, czy skrypt ma shebang i przeczytać, że od tłumacza, a następnie uruchomić go z Znaleziony tłumacza.

Przykład

#!/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")
  • Zapisz go jako tryrunw $PATH(na przykład ~/bin, zrobić katalog, jeśli nie istnieje, wylogować iz powrotem), sprawiają, że wykonywalny . Następnie uruchom:

    tryrun /path/to/nonexecutablescript

    wywołuje (testowany) poprawny interpreter na moich plikach wykonywalnych pythoni bashskryptach.

Wyjaśnienie

  • Skrypt po prostu odczytuje pierwszy wiersz skryptu, usuwa #!i używa reszty do wywołania tłumacza.
  • Jeśli nie zadzwoni do poprawnego tłumacza, podniesie albo a PermissionErroralbo FileNotFoundError.

Uwaga

Rozszerzenie ( .sh, .pyetc) w ogóle nie odgrywa żadnej roli w określaniu odpowiedniego tłumacza w systemie Linux.


(* Oczywiście możliwe jest opracowanie „inteligentnego” algorytmu zgadywania w celu określenia składni na podstawie kodu).

Jacob Vlijm
źródło
OK, więc oznacza to, że chociaż Linux ma gdzieś zaimplementowaną ekstrakcję shebang (aby mógł wybrać poprawny interpreter dla wykonywalnych skryptów), nie jest on dostarczany jako samodzielna standardowa komenda.
Aivar,
@Aivar wyodrębnianie shebang nie jest problemem, ale uruchomienie kodu bez niego jest całkowicie możliwe.
Jacob Vlijm,
@Aivar Ah, rozumiem, co masz na myśli. Jeśli skrypt jest wykonywalny i działa bez języka w poleceniu, skrypt wywołuje interpreter, a nie na odwrót.
Jacob Vlijm,
1
@JacobVlijm Nie powiedziałbym, że „skrypt wywołuje interpreter”, bardziej jak „jądro Linuksa przyjmuje linię shebang, aby dowiedzieć się, który interpreter ma wywołać podczas wykonywania skryptu”.
Paŭlo Ebermann
@ PaŭloEbermann Thanks! Prawda oczywiście. Jądro i tak wykonuje całą procedurę, ale w przenośni, i dla lepszego zrozumienia, lepiej powiedzieć, że skrypt może „zadbać” o to, który tłumacz wywołać (i faktycznie to zrobić). Nie jestem pewien co do tego sformułowania, ale chciałbym opisać to tak, jakby inicjatywa była w skrypcie, podczas gdy jądro faktycznie wykonuje to zadanie.
Jacob Vlijm,
6

Możesz to osiągnąć za pomocą takiego skryptu:

#!/bin/bash

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

A zatem:

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

Nie polecam tego robić. Uprawnienia istnieją z jakiegoś powodu. Jest to program do podważania uprawnień.

Zauważ, że obsługa shebang jest funkcją jądra (w kodzie źródłowym Linuksa - fs/binfmt_script.c). Zasadniczo proces wywoływania skryptu bezpośrednio nie wie o tym #!- jądro używa go do ustalenia, że ​​musi uruchomić interpreter.

szczupły
źródło
2
Zawsze zakładałem, że jest to funkcja powłoki, NIE jądra. Nauczyłem się dziś czegoś nowego.
Boatcoder
1
@boatcoder - Ponieważ jesteś zainteresowany, dodałeś link do tego, gdzie obsługuje go kod źródłowy Linuksa.
szczupły,