Шебанг, починаючи з "//"?


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

Я не чув про shebang починаючи з //. І він все ще працює, коли я вставляю порожній рядок у верхній частині сценарію. Чому цей сценарій працює?


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

2
слідкуючи за коментарями @ g-man та Jörg нижче, і відповідно до відповіді gilles ( unix.stackexchange.com/a/1919/27616 ), цей трюк слід використовувати ///....замість того, //...щоб бути найбільш сумісним!
Олів'є Дулак

1
Це не буде правильно обробляти аргументи (або розташування в каталозі) з пробілами без додаткових лапок:go run "$0" "$@"
Чарльз Даффі

Відповіді:


71

Це не шебанг, це просто сценарій, який запускається оболонкою за замовчуванням. Оболонка виконує перший рядок

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

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

Але навіщо починати //замість справедливого /або правильного шебангу #!?

Це тому, що для файлу повинен бути дійсний сценарій go, або go буде скаржитися. По ходу символи //позначають коментар, тому go бачить перший рядок як коментар і не намагається його інтерпретувати. Символ, #однак, не позначає коментар, тому звичайний шебанг призведе до помилки при переході інтерпретувати файл.

Ця причина синтаксису полягає лише в тому, щоб створити файл, який є і сценарієм оболонки, і сценарієм go, не переходячи на інший.


10
Це обробляється ядром, а не оболонкою; див. відповідь Гілла на те, як Linux обробляє кілька розділювачів шляху (/ home //// ім'я користувача /// файл) .
G-Man

3
@HermanTorjussen Feature - синакс шляхів досить чітко визначений, що дозволяє безліч корисних варіантів - і з потужністю надходить складність: /як суфікс шляху визначається як /.; Коли aне є симпосилання, aце те саме, a/що те саме, що і a/.Thera - це випадки, коли шлях може отримати додатковий /без зміни значення. При виведенні канонічного шляху відбувається крок нормалізації, стискаючи послідовну косу рису до однієї. Звичайно, це не є чистою частиною формального синтаксису.
Volker Siegel

13
Власне, POSIX говорить, що декілька косої риски є такою ж, як і одна коса коса риса, за винятком випадків, коли рівно дві косої риски є точно на самому початку шляху. Як це має місце тут. У цьому випадку інтерпретація шляху залежить від реалізації: "Якщо ім'я шляху починається з двох послідовних символів <слєш>, перший компонент, що слідує за провідними символами <косого>, може бути інтерпретований визначеним способом, хоча більш ніж два провідні символи <slash> розглядаються як один <slash> символ ".
Йорг W Міттаг

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 .

Зауважте також, що в скрипті $ @ є правильна помилка замість цього "$ @", тому що в іншому випадку, якщо будь-який параметр містить пробіли, він буде розділений на багато параметрів. Наприклад, ви не можете передавати ім'я файлу з пробілами, якщо ви не використовуєте "$ @"

Цей конкретний сценарій, очевидно, спирається на ідею, що "//" дорівнює "/"


9
"// просто ігнорується в шляхах" - Це не гарантується: "Якщо ім'я шляху починається з двох послідовних символів <слєш>, перший компонент, що слідує за провідними символами <косою>, може бути інтерпретований у визначеному реалізацією порядку" ( pubs .opengroup.org / onlinepubs / 9699919799 / basedefs /… )
Jörg W Mittag

Дуже цікава, оновлена ​​відповідь.
gena2x

1
... AFS зокрема реалізується // по-різному, але це вже не часто.
Чарльз Даффі

0

Це буде працювати для C ++ (і C, якщо це дозволяє // для коментарів)

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.