Объясните кривизну Perl “преамбула”

Руководство 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;

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

14
задан AstroCB 30 August 2014 в 21:10
поделиться

2 ответа

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

В Perl три строки образуют одну инструкцию, завершенную ; , вида

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

Поскольку сценарий только что запущен, $ running _ under _ some _ shell является undef , что является ложным, и evals никогда не выполняются Это не операция.

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

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

Как sh, так и csh понимают & & , что означает «выполнить предыдущую команду, затем выполнить следующую команду, только если предыдущая команда вышла 0». Так что только sh будет выполнять eval 'exec perl -wS $0 $ {1 + «$ @»}' . 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 + «$ @»} вместо более обычного «$ @» для работы вокруг ошибки в какой-то древней версии оболочки Борна. Они имеют в виду одно и то же. Подробности можно прочитать в пояснении Беннетта Тодда (скопировано в ответ gbacon).

27
ответ дан 1 December 2019 в 07:19
поделиться

Из коллекции Тома Кристиансена Far More Than Everything You've Ever Wanted to Know About ...:

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

Newsgroups: comp.lang.tcl,comp.unix.shell
From: bet@ritz.mordor.com (Bennett Todd)
Subject: Re: "$@" против ${1+"$@"}
Followup-To: comp.unix.shell
Date: Tue, 26 Sep 1995 14:35:45 GMT
Message-ID:

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

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

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

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

 "$@"

расширится до если нет args:

"""

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

${varname+value}

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

${1+"$@"}

означает ровно то же самое, что и простая

"$@"

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

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

-- 
-Bennett
bet@mordor.com
http://www.mordor.com/bet/
10
ответ дан 1 December 2019 в 07:19
поделиться
Другие вопросы по тегам:

Похожие вопросы: