Может ли кто-нибудь объяснить эту строку shebang, которая использует sh, а затем выполняет exec perl?

Ваше устройство работает постоянно в течение нескольких минут или есть ли регулярные паузы при передаче?

Если есть паузы, вы можете принудительно пустить буферы и кеш в ядре, чтобы это действие не будет мешать передачам DMA. В качестве альтернативы вы можете настроить ядро ​​на интервал BDFLUSHR в 1 секунду, чтобы у ядра было меньше данных для записи каждый раз, когда оно решает очистить буферы.

Если вам нужно обеспечить непрерывную работу, вам понадобится ОЗУ с большим количеством каналов, чтобы ЦП и ваше устройство могли одновременно обращаться к памяти (как оказалось, у вас уже есть 4-канальный контроллер памяти). Убедитесь, что вы настроили ОЗУ в автономном режиме , если эта опция доступна. Убедитесь, что вы установили аналогичные модули DRAM в 4 слота, соответствующие каналам памяти, чтобы ваш контроллер памяти действительно мог работать в 4-канальном режиме.

6
10.09.2018, 08:59
2 ответа

Идея состоит в том, что команда evalдействительна как в оболочке, так и в Perl, с той разницей, что новая строка завершает команду в оболочке, но не в Perl. Вместо этого Perl читает следующую строку, в которой добавляется условие if 0, фактически отрицающее всю команду.

(Perl поддерживает несколько таких «обратных» структур для сокращений, например. вы можете написать next if $_ == 0вместо if ($_ == 0) { next }или print for @aвместо for (@a) { print }.)

Если сценарий запускается оболочкой, оболочка обрабатывает evalи заменяет его интерпретатором Perl, присваивая ему имя сценария($0)и его аргументы($@)в качестве параметров.

Затем запускается Perl, читает eval, пропускает его (из-за if 0), а затем продолжает выполнение оставшейся части скрипта.


Вот как это должно работать. На практике вы получаете ошибку из-за двух вещей: :1 )Perl читает саму строку hashbang и 2 )способ, которым Linux обрабатывает строки hashbang.

Когда Perl запускает скрипт с хэш-бангом, он воспринимает его не просто как комментарий. Вместо этого он интерпретирует любые параметры Perl, указанные в хэш-банге (, которые вы можете иметь #!/usr/bin/perl -Wlnи т. д. ), но он также проверяет интерпретатор и выполняет его, если сценарий не должен запускаться Perl!

Попробуйте, например. это:

$ cat > hello.sh
#!/bin/bash
echo $BASH_VERSION 
$ perl hello.sh
4.4.12(1)-release

Это фактически запускает Bash.

Таким образом, комментарий #perlдолжен сказать Perl, что да, на самом деле предполагается, что Perl запускает его, чтобы он не запускал оболочку снова .

Однако,Linux дает все после имени интерпретатора в качестве одного аргумента, поэтому, когда вы запускаете скрипт, он запускается /bin/shс двумя аргументами -- # perl, to stop loopingи scriptname.pl. Первый начинается с тире, но это не совсем --, поэтому и Dash, и Bash пытаются интерпретировать его как опции. Это неверно, поэтому вы получите сообщение об ошибке, как если бы вы попытались запустить bash ---или bash "-- #perl".

В других системах, которые разделяют аргументы в строке hashbang, -- #perlвыдаст оболочке --, #perlи попытается найти файл с именем #perl. Это снова не сработает. Но, по-видимому, существуют/были некоторые системы, которые принимают знаки #в качестве маркеров комментариев в строке #!и/или передают только первый аргумент. (См. страницу Свена Масчека по этому вопросу . )В этих системах это может сработать.


Несколько лучший рабочий вариант приведен вperlrun(адаптированном):

#!/usr/bin/perl
eval 'exec /usr/bin/perl -S $0 ${1+"$@"}'
    if $running_under_some_shell;
print("Perl $^V\n");

Запуск этого с bash./script.plфактически запускает Perl и печатает (, например.)Perl v5.24.1.($running_under_some_shell— это просто неопределенная переменная, которая по умолчанию имеет значение false. if 0было бы чище, но не так описательно.)


Как сказано в вашей цитате, все это требуется только в старых системах, где #!не работает должным образом. В некоторых случаях он вообще не поддерживается, и запрос оболочки на запуск двоичного файла, отличного от -, всегда запускает его в оболочке. В других есть ограничение на длину пути в строке hashbang, поэтому #!/really/veeeery/long/path/to/perlне будет работать.

Просто поместите хэш-банг #!/usr/bin/perlна любую современную систему.

8
27.01.2020, 20:24

Решить /bin/sh :0 :Недопустимая опция--

Самое простое решение, позволяющее избежать ошибки, состоит в том, чтобы иметь только один аргумент для shвызова (в некоторых системах ), а также, по-видимому, добавить параметр -xв вызов perl [d] . И я говорю кажется, потому что это зависит от системы, ядра, оболочки и Perl, используемых для вызова скрипта.

Лучше использовать что-то вроде:

#!/bin/sh --
eval 'exec perl -x -S $0 ${1+"$@"}'

#!/usr/bin/perl
print("Perl $^V\n");

В приведенном выше скрипте единственная выполняемая команда perl — распечатать версию.

Краткое описание

Единственная причина использовать что-то подобное объясняется в книге (стр. 21):

Finally, if you are unfortunate enough to be on an ancient Unix system that doesn’t support the magic #! line, or if the path to your interpreter is longer than 32 characters (a built-in limit on many systems), you may be able to work around it......

Это уродливый и запутанный способ замены вызова #!/usr/bin/perl.

На той же странице 21 находится сценарий, который вы разместили. Следующий сценарий по этой проблеме находится на странице 577 (, продолжайте читать )

.

Примечание :Включение perlимени (слова )в первую строку необходимо только в том случае, если скрипт в любой момент будет вызываться как perl./script. В этом случае это слово остановит зацикливание Perl. На самом деле второй пример из книги (стр. 577 )— это:

#!/bin/sh -- # -*- perl -*- -p
eval 'exec perl -S $0 ${1+"$@"}'
if 0;

В котором часть комментария(#--perl---p )объясняется в книге как интерпретируемая perl (и по большей части игнорируется ).

Однако предполагается, что вызов оболочки #!/bin/shдопускает более одного(--)аргумента. В некоторых системах это не так. Лучше переместить на следующую строку (, если это требуется для emacs ), и/или удалить:

#!/bin/sh -- # 
# -*- perl -*- -p
eval 'exec perl -S $0 ${1+"$@"}'
if 0;

Портативный способ вызова perl

Чтобы понять его основы, первое, что нужно понять, это то, что сценарий *может **обрабатываться дважды: один раз оболочкой, а затем интерпретатором, вызываемым оболочкой, в данном случае perl.

Поймите, что скрипт может называться как sh script, bash./script, просто как ./scriptили,если путь включает текущий рабочий каталог, как scriptи даже как /usr/bin/perl scriptили как perl script. В DOS, Unix, Windows, Linux и т. д.

Как сделать так, чтобы вызов perl работал во всех системах?

Сторона кожуха

Этот код (кат./скрипт):

#!/bin/sh -- 
eval 'exec perl -S $0 ${1+"$@"}'
if 0;

Будет интерпретироваться почти любой оболочкой (, называемой как ./scriptили shell./scriptследующим образом:

  1. Ядро прочитает первую строку, идентифицирует ее как шебанг и запустит исполняемый файл оболочки, который находится в /bin/shфайле (или ссылке ). Кроме того, ядро ​​будет принимать и обрабатывать --как аргумент, означающий «конец опций» [a] [b] . Это строка:

    #!/bin/sh --
    
  2. Оболочка (, предполагающая наличие оболочки в /bin/sh), прочитает и выполнит первую (не -не закомментированную )строку:

    eval 'exec perl -S $0 ${1+"$@"}'
    

    Первое слово (eval )преобразует список аргументов в командную строку для выполнения удаления одного уровня цитирования. То есть он будет преобразован в эту командную строку:

    exec perl -S $0 ${1+"$@"}
    

    Первое слово этой (новой )командной строки (, то есть :exec), сообщит ядру заменить запущенный процесс исполняемым файлом perl(, искомым в PATH )и аргументами которые являются результатом расширения остальной части командной строки.

Первый аргумент(-S)указывает Perl также искать исполняемый скрипт, имя которого указано в следующем аргументе($0). Аргумент $0— это (обычно )имя ранее загруженного сценария оболочки (, содержащего код, который вы разместили, он же./script).

Следующий аргумент:${1+"$@"}означает :, если аргумент $1существует и не равен нулю,(${1+...})замените весь этот аргумент результатом расширения "$@". Расширение "$@"— это список всех аргументов загруженного скрипта.

Вы можете увидеть, какие аргументы задаются в этой командной строке, проверив:

    #!/bin/sh -
    eval 'exec /usr/bin/printf "<%s> " -S $0 ${1+"$@"}'

Вызов этого скрипта:

    $ ./script one two t33 f44 "f55 s66"
    <-S> <./script> <one> <two> <t33> <f44> <f55 s66>

Таким образом, эта строка в исходном скрипте будет вызывать perl (эквивалентно )как:

    $ perl -S./script one two t33 f44 "f55 s66"

Этот вызов perl заменяет исполняемый сценарий оболочки (exec ).

Жемчужная сторона

Затем Perl загружает скрипт и повторно -интерпретирует его содержимое.
Комментарии в Perl также начинаются с#
Таким образом, первая строка (shebang )является комментарием к Perl.

Вторая строка:eval 'exec perl -S $0 ${1+"$@"}'будет интерпретирована perl (как evalдопустимая команда Perl ), если следующая строка if 0;, где , а не . Эта третья строка заставляет Perl , а не выполнять вторую строку. Очень длинный способ написания nop.

Остальная часть сценария затем интерпретируется и выполняется Perl.

Обратите внимание, что вся эта конструкция представляет собой очень сложный способ записи:

#!/usr/bin/perl

Зачем это вообще нужно?

Для установки ПУТИ

Пример "hello world" , аналогичный и использующий параметр perl -xкак , обычно рекомендуемый на сайте perldoc .

Добавление вызова -x к perl гарантирует, что perl сам будет искать #!/usr/bin/perlшебанг (, который может быть на много строк ниже первой строки ), чтобы начать выполнение скрипта. Это -xтакже сделает if 0излишним.

#!/bin/sh -
eval 'PATH="/usr/bin:/bin:/usr/local/bin:$PATH";: \
;exec perl -x -S -- "$0" ${1+"$@"};#'if 0;

#!/bin/perl -w
# Above is magic header... real Perl code begins here
use strict;
use warnings;
print "hello world!\n";

Это установит более переносимый PATH для поиска исполняемого файла perl. Можно установить больше переменных, чтобы сделать вызов perl более надежным (при необходимости ).

[d] Опция -xperl будет искать строку shebang с именем perl :#!/bin/perl -w, чтобы начать интерпретацию скрипта из perl. Это делает разделение между оболочкой и perl более четким.

Этот скрипт будет работать, если он вызывается как:

$./script
hello world!

$ sh./script
hello world!

$ perl./script
hello world!

, но вызовет сбой из оболочки csh. Это решается добавлением eval '(exit $?0)'в качестве теста и нового исполняемого файла в виде:

#!/bin/sh --
eval '(exit $?0)' && eval 'PATH="/usr/bin:/bin:/usr/local/bin:$PATH";: \
     ;exec perl -x -S -- "$0" ${1+"$@"};#'if 0;
exec 'exec perl -x -S -- "$0" $argv:q;#'.q

Волшебный заголовок для запуска Perl-скриптов
Связанное длинное описание.

[a] Вся строка после #!/bin/shможет интерпретироваться только как один аргумент. В таком случае вся строка -- # perl, to stop loopingможет считаться ошибкой. Как если бы bash напрямую загружал скрипт при выполнении ./script(, в то время как работающей оболочкой является bash ). Подробнее читайте в поиске «Разделение аргументов» .

[b] В очень редких случаях, когда полезно использовать --Вместо этого более удобно использовать -, как объяснено в этом вопросе .

1
27.01.2020, 20:24

Теги

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