Настройка параметров bash в составной команде

9

Я обнаружил, что установка параметра extglobоболочки в составном соединении приводит к отказу последующих антиглобов. Должны ли параметры оболочки быть установлены вне составных команд? Я не видел указаний на такое требование в справочных страницах bash.

Как пример, следующий скрипт работает нормально (печатает a.0 a.1):

#!/bin/bash
touch a.0   a.1 \
      a.b.0 a.b.1
shopt -s extglob
ls "a."!(b*)

Однако, если последние две строки выполняются как составная команда, сценарий завершается ошибкой со следующей ошибкой:

syntax error near unexpected token `('
`    ls "a."!(b*)'

Это было протестировано с использованием версий bash от 4.2 до 4.4 и с помощью различных составных команд :

(1) условно - if

#!/bin/bash
touch a.0   a.1 \
      a.b.0 a.b.1
if true; then
    shopt -s extglob
    ls "a."!(b*)
fi

(2) брекеты - { }

#!/bin/bash
touch a.0   a.1 \
      a.b.0 a.b.1
{
    shopt -s extglob
    ls "a."!(b*)
}

(3) подоболочка - ( ):

#!/bin/bash
touch a.0   a.1 \
      a.b.0 a.b.1
(
    shopt -s extglob
    ls "a."!(b*)
)

Во всех случаях, если команда shoptперемещается за пределы составной команды, сценарий завершается успешно.

user001
источник

Ответы:

14

Ни одна часть составной команды не выполняется до тех пор, пока вся команда не будет проанализирована, что косвенно задокументировано в разделе « Операция оболочки » данного руководства: все токенизации и синтаксический анализ выполняются до выполнения команды «». Включение extglobизменяет синтаксис языка, добавляя новые операторы сопоставления с образцом, которые распознаются во время токенизации.

Поскольку shoptкоманда не была выполнена в момент !(достижения, она рассматривается как попытка расширения истории ( !) или как управляющий оператор (, а не !(как отдельный элемент (то же самое для @(и т. Д., За исключением того, что нет расширения истории). там).

Когда синтаксический анализ достигает этого маркера, любой случай, как правило, будет ошибкой, хотя !(...)в начале строки или после timeэто конвейер с отрицательной оболочкой. « unexpected token `('» означает, что в этом месте он не может принять выражение «подоболочки».

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

shopt -s extglob
f() { ls "a."!(b*) ; }
shopt -u extglob
(
    shopt -s extglob
    f
)

Это очень неуклюже.


Тот же эффект происходит, если вы создаете псевдоним в составной команде, потому что они также обрабатываются перед анализом:

if true
then
    alias foo=echo
    foo bar
fi
foo xyz

будет печатать foo: command not found, а затем xyz, так как псевдоним будет создан, но не доступен , пока ifне будет сделано.

Майкл Гомер
источник
Спасибо - я не осознавал, что составные команды перед выполнением анализировались как единое целое. Поведение теперь имеет смысл.
user001
3
@ user001 Я бы не сказал, что это имеет смысл. Переключение режима лексера во время синтаксического анализа не является чем-то неслыханным, и другие языки любят Cили perlмогут делать это хорошо. Обратите внимание, что не быть частью составной команды недостаточно, shopt -s extglobтакже должно быть в отдельной строке. Смотрите также обсуждение и примеры здесь
mosvy
@mosvy: Я имел в виду, что наблюдаемое поведение имеет смысл, как только понимаешь, что bash анализирует составную команду как единое целое (не то, что ограничения синтаксического анализатора обязательно разумны). Спасибо за ссылку на обсуждение в другом посте.
user001