Объясните коварность "преамбулы" Перла,

Руководство Perl описывает абсолютно коварную конструкцию, которая будет работать под любым из csh, sh или Perl, например:

eval '(exit $?0)' && eval 'exec perl -wS $0 ${1+"[email protected]"}'
    & eval 'exec /usr/bin/perl -wS $0 $argv:q'
    if $running_under_some_shell;

Действительно ли по-настоящему... может кто-нибудь объяснить, как это работает?

Ответы

Ответ 1

Идея состоит в том, что эти три строки выполняют 3 разные вещи, если их оценивают в стандартной оболочке Bourne (sh), оболочке C (csh) или Perl. Этот хак нужен только для систем, которые не поддерживают указание имени интерпретатора, используя строку #! в начале script. Если вы выполните Perl script, начиная с этих трех строк в качестве оболочки script, оболочка запустит интерпретатор Perl, передав ему имя файла script и аргументы командной строки.

В Perl три строки образуют один оператор, завершенный символом ; формы

eval '...' && eval '...' & eval '...' if $running_under_some_shell;

Поскольку script только что начался, $running_under_some_shell есть undef, что является ложным, и оценки не выполняются. Это не-op.

Коварная часть состоит в том, что $?0 анализируется по-разному в sh против csh. В sh это означает $? (статус выхода последней команды), за которым следует 0. Поскольку предыдущей команды нет, $? будет 0, поэтому $?0 будет оцениваться как 00. В csh, $?0 - специальная переменная, которая равна 1, если известно текущее имя входного файла, или 0, если это не так. Поскольку оболочка считывает эти строки из script, $?0 будет 1.

Следовательно, в sh, eval '(exit $?0)' означает eval '(exit 00)', а в csh это означает eval '(exit 1)'. Параны указывают, что команда exit должна быть оценена в подоболочке.

Как sh, так и csh понимают &&, чтобы означать "выполнить предыдущую команду, а затем выполнить следующую команду, только если предыдущая команда вышла из 0". Таким образом, только sh будет выполнять eval 'exec perl -wS $0 ${1+"[email protected]"}'. csh перейдет к следующей строке.

csh игнорирует "&" в начале строки. (Я точно не знаю, что это значит для csh. Его цель - сделать это одним выражением с точки зрения Perl.) Затем csh переходит к оценке eval 'exec /usr/bin/perl -wS $0 $argv:q'.

Эти две команды очень похожи. exec perl означает заменить текущий процесс, запустив копию perl. -wS означает то же, что и -w (включить предупреждения) и -S (найдите указанный script в $PATH). $0 - имя файла script. Наконец, как ${1+"[email protected]"}, так и $argv:q создают копию текущих аргументов командной строки (соответственно в sh и csh).

Он использует ${1+"[email protected]"} вместо более обычного "[email protected]" для обхода ошибки в какой-то древней версии оболочки Bourne. Они означают одно и то же. Вы можете прочитать подробности в объяснение Беннета Тодда (скопировано в ответ gbacon).

Ответ 2

Из коллекции Тома Кристиансена Дальше, чем все, о чем вы когда-либо хотели знать...:

Почему мы используем eval 'exec perl $0 -S ${1+"[email protected]"}'

Группы новостей: comp.lang.tcl, comp.unix.shell
От: [email protected](Беннетт Тодд)
Тема: Re: "[email protected]" против ${1+"[email protected]"}
Followup-To: comp.unix.shell
Дата: Вт, 26 сен 1995 14:35:45 GMT
Идентификатор сообщения: < [email protected] >

(Это не вопрос TCL, это вопрос Bourne Shell, поэтому я перекрестно размещены и установите последующие действия, в comp.unix.shell).

Когда-то (или так происходит история), где-то была Bourne Shell который предложил два варианта интерполяции всей командной строки. Простейший был $*, который просто был во всех аргументах, потеряв любое цитирование защитил внутренние пробелы. Он также предложил "[email protected]", чтобы защитить пробельные. Теперь бит icko - это способ реализации "[email protected]". В этом раннем shell, двухсимвольная последовательность [email protected] будет интерполировать как

$1" "$2" "$3" "$4" ... $n

чтобы при добавлении окружающих котировок он завершил цитирование всего schmeer. Симпатичный, симпатичный, слишком симпатичный.... Теперь рассмотрим, какое правильное использование

"[email protected]"

будет расширяться, если нет аргументов:

""

То, что пустая строка - единственный аргумент длины ноль. Которые не так же, как и вообще никаких аргументов. Итак, кто-то придумал умное применение другая особенность Bourne Shell, условная интерполяция. Идиома

${varname+value}

расширяется до value, если varname установлен, и ничего в противном случае. Таким образом обсуждаемая идиома

${1+"[email protected]"}

означает точно, точно так же, как простой

"[email protected]"

без этой древней, чрезвычайно странной ошибки.

Итак, теперь вопрос: какие у снарядов была эта ошибка? Есть ли какие-либо снаряды поставляется с любой даже смутно недавней ОС, которая включала его?

-- 
-Bennett
[email protected]
http://www.mordor.com/bet/