Как работает этот шебанг, начинающийся с двойного дефиса (-)?

14

Я нашел следующий вид шебанга на странице RosettaCode:

--() { :; }; exec db2 -txf "$0"

Это работает для Db2, и аналогичная вещь для Postgres. Однако я не понимаю всей линии.

Я знаю, что двойная черта - это комментарий в SQL, и после этого он вызывает исполняемый файл Db2 с некоторыми параметрами, передавая сам файл как файл. Но как насчет скобок, фигурных скобок, двоеточия и точки с запятой, и как можно заменить настоящий шебанг #! ?

https://rosettacode.org/wiki/Multiline_shebang#PostgreSQL

AngocA
источник

Ответы:

18

Связанный: Какой интерпретатор оболочки выполняет сценарий без Шебанга?

Скрипт не имеет #!строки shebang / hashbang / , просто потому что двойной тире нет #!.

Однако сценарий будет выполняться оболочкой (см. Выше связанный вопрос и ответы), и в этой оболочке, если -допустимый символ в имени функции, строка объявляет вызываемую функцию оболочки --, которая ничего не делает (хорошо, она запускается :, который ничего не делает ) и который никогда не называется.

Функция в более распространенной многострочной нотации (просто чтобы сделать ее более очевидной, как она выглядит, так как ее нечетное имя как бы скрывает тот факт, что это на самом деле функция):

-- () {
  :
}

Единственная цель определения функции - иметь строку, которая действительна в сценарии оболочки и в то же время является допустимой командой SQL (комментарий). Этот вид кода называется полиглотом .

После объявления фиктивной функции оболочки сценарий при выполнении интерпретатором сценария оболочки использует execдля замены текущей оболочки процесс, полученный в результате выполненияdb2 -txf "$0" , который будет аналогичен использованию db2 -txfв имени пути сценария из командной строки.

Этот трюк, вероятно, не будет надежно работать в системах, где используются dashили другие ashоболочки на основе, оболочка yashБорна ksh88или ksh93используется как/bin/sh , поскольку эти оболочки не принимают функции, имя которых содержит тире.

Также связано:


Я полагаю, что следующее также будет работать (не проверено):

--() { exec db2 -txf "$0"; }; --
Кусалананда
источник
@ilkkachu Лучше сейчас?
Кусалананда
1
о да! И спасибо, что напомнили мне, как это называется. :)
ilkkachu
6

Как уже сказал @Kusalananda, этот трюк сломан, и он не будет работать во всех оболочках.

Вот мой взгляд на это:

--/.. 2>/dev/null; exec db2 -txf "$0"

Первая команда должна завершиться ошибкой, даже если файл / каталог с именем -- в текущем каталоге существует указанный каталог и любые ошибки будут закрыты командой 2>/dev/null; Затем оболочка продолжит выполнение второй команды exec.

Дядя билли
источник
Это все еще не очень портативный. Это неверный скрипт, и вы все еще полагаетесь на вызывающую оболочку, чтобы обойти тот факт, что ядро ​​откажется запускать скрипт и вернется, ENOEXECесли вы попытаетесь это сделать. Попробуйте запустить скрипт, straceчтобы понять, что я имею в виду.
kasperd
@kasperd, он все еще должен быть переносимым, оболочка должна запускать сценарий как сценарий оболочки, если exec()не работает на нем. «Если функция execl () завершается ошибкой из-за ошибки, эквивалентной ошибке [ENOEXEC], оболочка должна выполнить команду, эквивалентную вызову оболочки с именем команды в качестве первого операнда, ...» (см. Pubs.opengroup .org / onlinepubs / 9699919799.2018 / редакция / утилиты /… )
ilkkachu
@ilkkachu Но сценарии не всегда выполняются из оболочки. Если вы попытаетесь использовать скрипт в любом другом контексте, где исполняемый файл будет работать, он потерпит неудачу. Кроме того, оболочки не согласны с тем, какой интерпретатор использовать. Таким образом, ваш скрипт теперь будет вести себя по-разному или вообще не работать в зависимости от контекста, из которого он вызывается.
kasperd
@kasperd, ну конечно, это не сработает, если вы сделаете exec()это непосредственно из чего-то другого, кроме оболочки. Но что это будет за случай? Возможно, вы захотите запустить сценарий cronили что-то подобное, но я думаю, что он в любом случае запускает все через оболочку, и даже если нет, db2 -txf /path/to/scriptв этом случае просто разобраться , так как вам нужно сделать это только один раз. Работа с сокращениями в основном полезна для интерактивной оболочки. Но, конечно, отдельный скрипт-обертка может быть более надежным.
ilkkachu
1
@kasperd Я не буду раздражать вас документами и стандартами; просто попробуйте! echo 'int main(int c,char**a){execvp(a[1],a+1);}' | cc -include unistd.h -xc -; echo echo yeah > a.sh; chmod 755 a.sh; ./a.out ./a.sh; PATH=`pwd` ./a.out a.sh
Дядя Билли