Объясните хитрость Perl в «преамбуле» - PullRequest
14 голосов
/ 22 февраля 2010

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

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

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

Ответы [ 2 ]

27 голосов
/ 22 февраля 2010

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

В Perl три строки образуют одно утверждение, оканчивающееся ;, в форме

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

Поскольку скрипт только что начался, $running_under_some_shell равно undef, что ложно, и уловки никогда не выполняются. Это неоперация.

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

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

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

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

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

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

10 голосов
/ 22 февраля 2010

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

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

Группы новостей: comp.lang.tcl, comp.unix.shell
От: bet@ritz.mordor.com (Беннетт Тодд)
Тема: Re: "$@" против ${1+"$@"}
Follow-To: comp.unix.shell
Дата: вторник, 26 сентября 1995 г. 14:35:45 GMT
Идентификатор сообщения:

(Это на самом деле не вопрос TCL; это вопрос Bourne Shell; Перекрестная публикация и установка продолжений в comp.unix.shell).

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

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

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

"$@"

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

""

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

${varname+value}

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

${1+"$@"}

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

"$@"

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

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

-- 
-Bennett
bet@mordor.com
http://www.mordor.com/bet/
...