Что означает $ {1 + «$ @»} в сценарии оболочки и чем он отличается от «$ @»?

43

В документации по Perl perlrun (1) предлагает запустить сценарии Perl с использованием двуязычного заголовка оболочки / Perl:

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

Что ${1+"$@"}значит? Я попытался использовать "$@"вместо этого (используя Bash как / bin / sh), и это, кажется, работает так же хорошо.


редактировать

Два ответа ниже говорят, что это должно быть ${1:+"$@"}. Мне известен ${parameter:+word}синтаксис («Использовать альтернативное значение»), описанный в bash (1). Тем не менее, я не уверен, потому что

  1. Оба ${1+"$@"}и "$@"работают просто отлично, даже когда нет параметров. Если я создаю simple.sh как

    #!/bin/sh
    eval 'exec /usr/bin/perl -x -S -- $0 "$@"'
        if 0;
    #!perl
    use Data::Dumper;
    print Dumper(\@ARGV);
    

    и вопрос.ш как

    #!/bin/sh
    eval 'exec /usr/bin/perl -x -S -- $0 ${1+"$@"}'
        if 0;
    #!perl
    use Data::Dumper;
    print Dumper(\@ARGV);
    

    Я могу заставить оба работать одинаково:

    $ ./question.sh 
    $VAR1 = [];
    $ ./question.sh a
    $VAR1 = [
              'a'
            ];
    $ ./question.sh a 'b c'
    $VAR1 = [
              'a',
              'b c'
            ];
    $ ./question.sh ""
    $VAR1 = [
              ''
            ];
    $ ./simple.sh 
    $VAR1 = [];
    $ ./simple.sh a
    $VAR1 = [
              'a'
            ];
    $ ./simple.sh a 'b c'
    $VAR1 = [
              'a',
              'b c'
            ];
    $ ./simple.sh ""
    $VAR1 = [
              ''
            ];
    
  2. Другие источники в Интернете также используют ${1+"$@"}, в том числе один хакер, который, кажется, знает, что он делает.

Возможно, ${parameter+word}недокументированный альтернативный (или устаревший) синтаксис для ${parameter:+word}? Может ли кто-нибудь подтвердить эту гипотезу?

200_success
источник
Выглядит как грауликс
Мартин Тома,

Ответы:

53

Это для совместимости с оболочкой Bourne. Оболочка Bourne была старой оболочкой, которая была впервые выпущена с версией 7 Unix в 1979 году и до середины 90-х годов была распространена, как и /bin/shв большинстве коммерческих Unices.

Это является предком большинства Bourne-подобных оболочек , как ksh, bashили zsh.

У него было несколько неловких особенностей, многие из которых были исправлены в kshдругих оболочках, и новая стандартная спецификация sh, одна из которых такова:

С оболочкой Bourne (по крайней мере, в тех вариантах, где она не была исправлена): "$@"расширяется до одного пустого аргумента, если список позиционных параметров пуст ( $# == 0), а не аргумент вообще.

${var+something}расширяется до «чего-то», если $varне установлено. Он четко задокументирован во всех оболочках, но его трудно найти в bashдокументации, так как вам нужно обратить внимание на это предложение:

Когда не выполняется раскрытие подстроки, используя формы, описанные ниже, bash тестирует параметр, который не установлен или имеет значение NULL. Пропуск двоеточия приводит к проверке только для неустановленного параметра .

Так ${1+"$@"}расширяется, "$@"только если $1set ( $# > 0), который работает вокруг этого ограничения оболочки Bourne.

Обратите внимание, что оболочка Bourne - единственная оболочка с этой проблемой. Современные shs (которые shсоответствуют спецификации POSIX sh(которой не является оболочка Bourne)) не имеют такой проблемы. Таким образом, вам нужно только, если вам нужен код для работы на очень старых системах, где /bin/shможет быть оболочка Борна вместо стандартной оболочки (обратите внимание, что POSIX не указывает местоположение стандарта sh, например, в Solaris до Solaris 11, /bin/shбыла все еще оболочкой Bourne (хотя у нее не было этой конкретной проблемы), в то время как normal / standard shнаходился в другом месте ( /usr/xpg4/bin/sh)).

В этой perlrunстранице perldoc есть проблема, которая $0не указана.

Смотрите http://www.in-ulm.de/~mascheck/various/bourne_args/ для получения дополнительной информации.

Стефан Шазелас
источник
Не могу воспроизвести в семейной реликвии. Вероятно, не относится к Solaris.
ormaaj
2
@ormaaj. Да, смотрите страницу, на которую я ссылался. Это было исправлено в SVR3, а Solaris / SunOS выше 5 перебазирован на SVR4. И на этой странице упоминается, что у 4.1.x также не было проблемы.
Стефан Шазелас
Ах я вижу. <3 страницы Mascheck.
ormaaj
3

Есть разница между:

command ""

а также

command

В одном вы передаете один аргумент, который является пустой строкой. Во втором случае передается ноль аргументов.

Для обоих, «$ @» будет приравнивать к тому же: "". Но использование ${1:+"$@"}будет ""для первого, и никакие аргументы не будут переданы для второго, что и было намерением.

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

: sshwrapper [command]
exec /usr/bin/ssh "${HOSTNAME:-localhost}" "$@"

Это попыталось бы запустить "" на удаленном хосте (который просто возвращается), это не будет интерактивная оболочка.

: sshwrapper [command]
exec /usr/bin/ssh "${HOSTNAME:-localhost}" ${1:+"$@"}

Запустил бы интерактивную оболочку на удаленном хосте, потому что exec правильно интерпретировал бы переменную как ничто, а не пустую строку.

Прочтите об использовании "$ {параметр: + слово}" ("Использовать альтернативное значение") и подобных строк расширения переменных на страницах справки bash.

Arcege
источник
кажется, что это применимо только к оболочке Bourne, а не к любой shреализации, соответствующей POSIX (см. другой ответ).
törzsmókus
4
Нет, ${1:+"$@"}расширился бы без аргументов, если бы $1был пустым. Вы хотите ${1+"$@"}. В основном у вас все наоборот.
Стефан Шазелас
0

Я резюмировал ответ Стефана Шазеля:

  • $ {1: + "$ @"} 'тест, если $ 1 нулевой или не установлен
  • $ {1 + "$ @"} 'тест, если $ 1 не установлено

так что если использовать вторую с параметром "", это означает, что $ 1 равен нулю, но он не проверяет, является ли он нулевым или нет, он просто видит, что он уже установлен, тем не менее, он пустой или нет, поэтому он будет расширять $ @ , но вы используете $ {1: + "$ @"} 'с "", он больше не будет расширяться $@.

Kevin
источник
7
Это, безусловно, резюмируется, хотя может и не прояснить ...
Archemar