Shebang dimulai dengan `//`?

60

Saya bingung tentang mengikuti skrip ( hello.go).

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

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

Itu bisa mengeksekusi. (pada MacOS X 10.9.5)

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

Saya belum pernah mendengar tentang shebang dimulai dengan //. Dan masih berfungsi ketika saya memasukkan baris kosong di bagian atas skrip. Mengapa skrip ini berfungsi?

kawty
sumber
//&>/dev/null;x="${0%.*}";[ ! "$x" -ot "$0" ]||(rm -f "$x";cc -o "$x" "$0")&&exec "$x" "$@" ...
REINSTATE MONICA -Jeremy Banks
2
mengikuti @ g-man dan komentar Jorg di bawah ini, dan menurut jawaban gilles ( unix.stackexchange.com/a/1919/27616 ), trik ini harus digunakan ///....alih-alih //...menjadi yang paling kompatibel!
Olivier Dulac
1
Ini tidak akan dengan benar menangani argumen (atau lokasi dalam direktori) dengan spasi tanpa tanda kutip lebih banyak:go run "$0" "$@"
Charles Duffy

Jawaban:

71

Ini bukan shebang, itu hanya skrip yang dijalankan oleh shell default. Shell mengeksekusi baris pertama

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

yang menyebabkan godipanggil dengan nama file ini, sehingga hasilnya adalah file ini dijalankan sebagai skrip go dan kemudian shell keluar tanpa melihat sisa file.

Tetapi mengapa mulai dengan //bukan hanya /atau shebang yang tepat #!?

Ini karena file tersebut harus skrip go yang valid, atau go akan mengeluh. Dalam go, karakter //menunjukkan komentar, jadi go melihat baris pertama sebagai komentar dan tidak berusaha menafsirkannya. #Namun karakternya , tidak menunjukkan komentar, jadi shebang yang normal akan menghasilkan kesalahan ketika pergi menafsirkan file.

Alasan sintaks ini adalah hanya untuk membangun file yang merupakan skrip shell dan skrip go tanpa ada yang menginjak yang lain.

casey
sumber
10
Ini ditangani oleh kernel, bukan shell; lihat jawaban Gilles untuk Bagaimana Linux menangani beberapa pemisah jalur (/ home //// nama pengguna /// file) .
G-Man Mengatakan 'Reinstate Monica'
3
Fitur @HermanTorjussen - synax path didefinisikan dengan cukup baik, memungkinkan banyak varian yang berguna - dan dengan kekuatan muncul kompleksitas: /sebagai akhiran path didefinisikan sebagai /.; Ketika abukan symlink, aadalah sama dengan a/yang sama dengan a/.Thera adalah kasus di mana jalan bisa mendapatkan tambahan /tanpa perubahan makna. Ketika mendapatkan jalur kanonik, ada langkah normalisasi yang mengontrak garis miring berurutan. Memang, itu bukan bagian yang bersih dari sintaks formal.
Volker Siegel
13
Sebenarnya, POSIX mengatakan bahwa beberapa garis miring sama dengan garis miring tunggal kecuali ketika ada dua garis miring tepat di awal jalan. Seperti halnya di sini. Dalam kasus itu, interpretasi path bergantung pada implementasi: "Jika pathname dimulai dengan dua karakter <slash> berturut-turut, komponen pertama yang mengikuti karakter <slash> utama dapat diinterpretasikan dalam implementasi yang ditentukan, meskipun lebih dari dua karakter <slash> utama harus diperlakukan sebagai karakter <slash> tunggal. "
Jörg W Mittag
11
Jadi, untuk membuatnya portabel kita harus menulis ///usr/bin/env go run $0 $@ ; exit...
Ruslan
1
@geek, shell keluar tetapi tidak sebelum meluncurkan penerjemah go. Go adalah mencetak hello world, bukan shell.
Casey
8

Ini berjalan karena secara default file executable diasumsikan sebagai skrip / bin / sh. Yaitu jika Anda tidak menentukan shell tertentu - itu adalah #! / Bin / sh.

// hanya diabaikan di jalur - Anda dapat menganggapnya sebagai '/' tunggal.

Jadi Anda dapat mempertimbangkan bahwa Anda memiliki skrip shell dengan baris pertama:

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

Apa yang dilakukan garis ini? Ini berjalan 'env' dengan paramenters 'go run $ 0 $ @'. ada 'go' adalah perintah dan 'jalankan $ 0 $ @' adalah argumen dan keluar dari skrip setelahnya. $ 0 adalah nama skrip ini. $ @ adalah argumen skrip asli. Jadi baris ini berjalan pergi yang menjalankan skrip ini dengan argumennya

Ada detail yang cukup menarik, seperti yang ditunjukkan dalam komentar, bahwa dua garis miring didefinisikan implementasi, dan skrip ini akan menjadi POSIX-benar jika menetapkan tiga garis miring atau lebih. Rujuk ke http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html untuk perincian tentang cara penanganan garis miring di jalur.

Perhatikan juga bahwa ada kesalahan lain dalam skrip $ @ itu benar untuk menggunakan "$ @" sebagai gantinya, karena jika tidak ada parameter yang mengandung spasi maka akan dipecah menjadi banyak parameter. Misalnya Anda tidak dapat memasukkan nama file dengan spasi jika Anda tidak menggunakan "$ @"

Skrip khusus ini jelas bergantung pada gagasan bahwa '//' sama dengan '/'

gena2x
sumber
9
"// hanya diabaikan di jalur" - Itu tidak dijamin: "Jika nama path dimulai dengan dua karakter <slash> berturut-turut, komponen pertama yang mengikuti karakter <slash> terkemuka dapat ditafsirkan dalam cara yang ditentukan-implementasi" ( pub .opengroup.org / onlinepubs / 9699919799 / basedefs /… )
Jörg W Mittag
Sangat menarik, jawaban yang diperbarui.
gena2x
1
... AFS khususnya diimplementasikan // secara berbeda, tetapi tidak umum lagi.
Charles Duffy
0

Ini akan berfungsi untuk C ++ (dan C jika itu memungkinkan // untuk komentar)

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

Matthew Hannigan
sumber