Shebang zaczynający się od `//`?


60

Nie jestem pewien co do następującego skryptu ( hello.go).

//usr/bin/env go run $0 $@ ; exit

package main
import "fmt"
func main() {
    fmt.Printf("hello, world\n")
}

Można wykonać. (w MacOS X 10.9.5)

$ chmod +x hello.go
$ ./hello.go
hello, world

Od początku nie słyszałem o shebang //. I nadal działa, gdy wstawię pustą linię na górze skryptu. Dlaczego ten skrypt działa?


//&>/dev/null;x="${0%.*}";[ ! "$x" -ot "$0" ]||(rm -f "$x";cc -o "$x" "$0")&&exec "$x" "$@" ...
REINSTATE MONICA -Jeremy Banks

2
po komentarzach @ g-man i Jörga poniżej i zgodnie z odpowiedzią Gillesa ( unix.stackexchange.com/a/1919/27616 ), ta sztuczka powinna być używana ///....zamiast //...być najbardziej kompatybilną!
Olivier Dulac

1
To nie będzie poprawnie obsługiwać argumentów (lub lokalizacji w katalogu) ze spacjami bez dalszych cytatów:go run "$0" "$@"
Charles Duffy

Odpowiedzi:


71

To nie jest shebang, to tylko skrypt uruchamiany przez domyślną powłokę. Powłoka wykonuje pierwszą linię

//usr/bin/env go run $0 $@ ; exit 

co powoduje, goże jest wywoływana z nazwą tego pliku, więc wynik jest taki, że plik ten jest uruchamiany jako skrypt go, a następnie powłoka wychodzi bez patrzenia na resztę pliku.

Ale po co zaczynać //zamiast sprawiedliwego /lub właściwego seksu #!?

Wynika to z faktu, że plik musi być prawidłowym skryptem go, w przeciwnym razie go będzie narzekać. W go znaki //oznaczają komentarz, więc go widzi pierwszy wiersz jako komentarz i nie próbuje go interpretować. Znak ten #jednak nie oznacza komentarza, więc normalny shebang spowodowałby błąd, gdy go interpretuje plik.

Powodem tej składni jest po prostu zbudowanie pliku, który jest zarówno skryptem powłoki, jak i skryptem go, bez jednego kroku na drugim.


10
Obsługuje go jądro, a nie powłoka; zobacz odpowiedź Gillesa na temat tego, jak Linux obsługuje wiele separatorów ścieżek (plik / home //// nazwa użytkownika ///) .
G-Man,

3
@HermanTorjussen Feature - synx ścieżek jest dość dobrze zdefiniowany, co pozwala na wiele przydatnych wariantów - a wraz z mocą pojawia się złożoność: /ponieważ sufiks ścieżki jest zdefiniowany jako /.; Gdy anie jest dowiązaniem symbolicznym, ajest takie samo, jak a/to, co a/.Thera to przypadki, w których ścieżka może uzyskać dodatkowy /bez zmiany znaczenia. Podczas wyprowadzania ścieżki kanonicznej występuje krok normalizacyjny obejmujący kolejne ukośniki jednym. To prawda, że ​​nie jest to czysta część składni formalnej.
Volker Siegel

13
W rzeczywistości POSIX mówi, że wiele ukośników jest identycznych jak pojedynczy ukośnik, z wyjątkiem sytuacji, gdy dokładnie dwa ukośniki znajdują się dokładnie na samym początku ścieżki. Tak jak tutaj. W takim przypadku interpretacja ścieżki zależy od implementacji: „Jeśli nazwa ścieżki zaczyna się od dwóch kolejnych znaków <slash>, pierwszy składnik następujący po wiodących znakach <slash> może być interpretowany w sposób zdefiniowany w implementacji, chociaż więcej niż dwa wiodące znaki <slash> należy traktować jako pojedynczy znak <slash>. ”
Jörg W Mittag

11
Aby więc był przenośny, należy zamiast tego napisać ///usr/bin/env go run $0 $@ ; exit...
Ruslan

1
@geek powłoka wychodzi, ale nie przed uruchomieniem interpretera go. Go drukuje cześć, świecie, a nie powłokę.
Casey

8

Działa, ponieważ domyślnie plik wykonywalny przyjmuje się jako skrypt / bin / sh. To znaczy, jeśli nie określiłeś żadnej konkretnej powłoki - jest to #! / Bin / sh.

// jest po prostu ignorowany w ścieżkach - można uznać, że ma on wartość „single” /.

Możesz więc wziąć pod uwagę, że masz skrypt powłoki z pierwszym wierszem:

/usr/bin/env go run $0 $@ ; exit

Co robi ta linia? Działa „env” z paramentami „uruchom $ 0 $ @”. tam jest polecenie „go”, a „run $ 0 $ @” to argumenty i kończy skrypt. $ 0 to nazwa skryptu. $ @ to oryginalne argumenty skryptu. Więc linia biegnie dalej, która uruchamia ten skrypt z jego argumentami

Istnieją dość interesujące szczegóły, jak wskazano w komentarzach, że dwa ukośniki są zdefiniowane w implementacji, a skrypt ten stałby się poprawny POSIX, gdyby podał trzy lub więcej ukośników. Zobacz http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html, aby uzyskać szczegółowe informacje na temat tego, w jaki sposób ukośniki powinny być obsługiwane w ścieżkach.

Zauważ też, że istnieje inny błąd w skrypcie: $ @ zamiast tego poprawne jest użycie „$ @”, ponieważ w przeciwnym razie, jeśli jakikolwiek parametr zawiera spacje, zostanie podzielony na wiele parametrów. Na przykład nie możesz przekazać nazwy pliku ze spacjami, jeśli nie używasz „$ @”

Ten konkretny skrypt oczywiście opiera się na idei, że „//” jest równe „/”


9
„// jest po prostu ignorowany w ścieżkach” - nie jest to gwarantowane: „Jeśli nazwa ścieżki zaczyna się od dwóch kolejnych znaków <slash>, pierwszy składnik następujący po wiodących znakach <slash> może być interpretowany w sposób określony przez implementację” ( pubs .opengroup.org / onlinepubs / 9699919799 / basedefs /… )
Jörg W Mittag

Bardzo interesująca, zaktualizowana odpowiedź.
gena2x

1
... W szczególności AFS zaimplementowano // inaczej, ale to już nie jest powszechne.
Charles Duffy

0

Będzie to działać w C ++ (i C, jeśli C pozwala // na komentarze)

//usr/bin/env sh -c 'p=$(expr '"_$0"' : "_\(.*\)\.[^.]*"); make $p > /dev/null && $p'; exit

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.