Шебанг, начинающийся с `//`?

60

Я запутался в следующем скрипте ( hello.go).

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

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

Это может выполнить. (на MacOS X 10.9.5)

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

Я не слышал о Шебанге, начиная с //. И это все еще работает, когда я вставляю пустую строку в верхней части скрипта. Почему этот скрипт работает?

kawty
источник
//&>/dev/null;x="${0%.*}";[ ! "$x" -ot "$0" ]||(rm -f "$x";cc -o "$x" "$0")&&exec "$x" "$@" ...
ВОССТАНОВИТЬ МОНИКУ
2
следуя приведенным ниже комментариям @ g-man и Jörg, и, согласно ответу Жиля ( unix.stackexchange.com/a/1919/27616 ), этот прием следует использовать ///....вместо того, //...чтобы быть наиболее совместимым!
Оливье Дюлак
1
Это не будет правильно обрабатывать аргументы (или расположение в каталоге) с пробелами без дополнительных кавычек:go run "$0" "$@"
Чарльз Даффи

Ответы:

71

Это не шебанг, это просто скрипт, запускаемый оболочкой по умолчанию. Оболочка выполняет первую строку

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

что вызывает goзапуск с именем этого файла, поэтому в результате этот файл запускается как скрипт go, а затем оболочка завершает работу, не просматривая остальную часть файла.

Но зачем начинать с //вместо просто /или надлежащего притон #!?

Это потому, что файл должен быть действительным сценарием go, иначе go будет жаловаться. В go символы //обозначают комментарий, поэтому go видит первую строку как комментарий и не пытается ее интерпретировать. Символ, #однако, не обозначает комментарий, поэтому обычный шебанг может привести к ошибке, когда go интерпретирует файл.

Эта причина синтаксиса заключается в том, чтобы просто создать файл, который является одновременно сценарием оболочки и сценарием go, без единого шага на другом.

Casey
источник
10
Это обрабатывается ядром, а не оболочкой; см. ответ Жиля на « Как Linux обрабатывает несколько разделителей пути» (/ home //// username /// file) .
G-Man говорит: «Восстановите Монику»
3
Функция @HermanTorjussen - синтаксис путей довольно хорошо определен, допускает множество полезных вариантов - и с силой приходит сложность: /как суффикс пути определяется как /.; Когда aэто не символическая ссылка, aэто то же a/самое, что и a/.Тера, это случаи, когда путь может получить дополнительный /без изменения значения. При выводе канонического пути есть шаг нормализации, заключающийся в последовательных слешах к единице. Конечно, это не чистая часть формального синтаксиса.
Фолькер Сигел
13
На самом деле, POSIX говорит, что несколько слэшей - это то же самое, что и один слеш, за исключением случаев, когда в самом начале пути есть ровно два слеша. Как и здесь. В этом случае интерпретация пути зависит от реализации: «Если имя пути начинается с двух последовательных символов <slash>, первый компонент, следующий за начальными символами <slash>, может интерпретироваться способом, определяемым реализацией, хотя более чем два ведущих символа <slash> должны рассматриваться как один символ <slash>. "
Йорг Миттаг
11
Итак, чтобы сделать его переносимым, нужно вместо этого написать ///usr/bin/env go run $0 $@ ; exit...
Руслан
1
@geek оболочка завершается, но не перед запуском интерпретатора go. Go печатает привет мир, а не оболочку.
Кейси
8

Он запускается потому, что по умолчанию исполняемый файл считается скриптом / bin / sh. Т.е. если вы не указали какую-либо конкретную оболочку - это #! / Bin / sh.

// просто игнорируется в путях - вы можете считать, что это как '/'.

Таким образом, вы можете считать, что у вас есть скрипт оболочки с первой строкой:

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

Что делает эта строка? Он запускает 'env' с параметрами 'go run $ 0 $ @'. там 'go' - команда, а 'run $ 0 $ @' - аргументы и выход из скрипта впоследствии. $ 0 это имя скрипта. $ @ - это оригинальные аргументы скрипта. Таким образом, эта строка запускает Go, который запускает этот скрипт с его аргументами

Как отмечалось в комментариях, есть довольно интересные подробности о том, что две косые черты определяются реализацией, и этот сценарий станет POSIX-корректным, если он укажет три или более косых черт. Обратитесь к http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html за подробной информацией о том, как следует обрабатывать косые черты в путях.

Также обратите внимание, что в скрипте есть еще одна ошибка - $ @, вместо этого правильно использовать «$ @», потому что в противном случае, если какой-либо параметр содержит пробелы, он будет разделен на множество параметров. Например, вы не можете передать имя файла с пробелами, если вы не используете "$ @"

Этот конкретный сценарий, очевидно, опирается на идею, что «//» равно «/»

gena2x
источник
9
«// просто игнорируется в путях» - это не гарантируется: «Если имя пути начинается с двух последовательных символов <slash>, первый компонент, следующий за начальными символами <slash>, может интерпретироваться способом, определяемым реализацией» ( pubs .opengroup.org / onlinepubs / 9699919799 / basedefs /… )
Йорг Миттаг
Очень интересный, обновленный ответ.
gena2x
1
... AFS, в частности, реализован // по-другому, но это больше не распространено.
Чарльз Даффи
0

Это будет работать для C ++ (и C, если этот C позволяет // для комментариев)

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

Мэтью Ханниган
источник