#bash #brace-expansion
#bash #расширение фигурных скобок
Вопрос:
Я работаю над очень простым сценарием bash, и у меня возникла проблема с недооценкой того, почему устаревший $[] работает безупречно, в то время как $ (()), похоже, нарушает все это.
Код, на который я ссылаюсь, это:
for i in {1..10};
do
printf M $[{1..10}*i]
echo
done
В этой версии у меня нет проблем, но я не хотел бы использовать устаревшие элементы bash, поэтому я хотел переключиться на $ (()) .
К сожалению, как только я меняю свой код на:
printf M $(({1..10}*i))
Я получаю сообщение об ошибке:
./script_bash.sh: line 8: {1..10}*i: syntax error: argument expected (error token is "{1..10}*i")
Я был бы благодарен за некоторую помощь с этим…
Комментарии:
1.
$(())
является арифметическим выражением. Там не выполняется обычное расширение подстановочных знаков или фигурных скобок.
Ответ №1:
Настройка машины возврата к 1990 году.
В Bash реализован $[]
синтаксис для POSIX P1003.2d9 (около 1990 года), который был черновиком выпущенного P1003.2-1992. За два года между черновиком и стандартом POSIX вместо этого остановился на $(())
синтаксисе и поведении ksh88. Чет Рами (сопровождающий bash) сказал это еще в 2012 году:
Bash … реализовал $[…], потому что в то время не было другого синтаксиса, и чтобы получить некоторый опыт работы с арифметическим расширением в оболочке. Bash-1.14… перечислены обе формы арифметического расширения, но к моменту выпуска bash-2.0 в 1995 году в руководстве упоминалась только форма $((…)) .
Это наводит меня на мысль, что $[]
форма была экспериментальной, и у нее было определенное поведение (например, расширение фигурных скобок), которое было указано в забвении, когда POSIX принял $(())
синтаксис. Эти экспериментальные модели поведения были оставлены, поскольку в дикой природе уже были сценарии, полагающиеся на них (помните, прошло более 2 лет).
В том же потоке Чет дает понять, что $[]
форма устарела, но не устарела:
Теперь вряд ли стоит продолжать перетаскивать синтаксис $[…]. Это занимает всего несколько десятков байт кода. Я не планирую его удалять.
В текущем стандарте POSIX, C.2.6 Расширения слов> Арифметическое расширение, упоминается синтаксис (выделение мое):
В ранних предложениях использовалась форма $[expression] . Это было функционально эквивалентно «$ (())» текущего текста, но были выдвинуты возражения, что в KornShell 1988 года уже реализован «$ (())», и не было веских причин изобретать еще один синтаксис. Кроме того, синтаксис «$ []» имел незначительную несовместимость с шаблонами в операторах case.
Таким образом, реализованное поведение в bash не совсем соответствует спецификации, но, поскольку удалять его не планируется, я не вижу причин отказываться от его преимуществ, если оно аккуратно решает вашу проблему. Однако, как указано в комментарии @Barmar’s, было бы неплохо прокомментировать код и связать его здесь, чтобы будущие разработчики знали, что, черт возьми, вы имеете в виду!
Комментарии:
1. Хотя оно явно не является устаревшим (существует ли на самом деле список устаревших функций?), Тот факт, что оно даже не упоминается в руководстве, является убедительным признаком того, что его следует избегать. Даже если это никогда не исчезнет, другие программисты этого не поймут. Я программирую сценарии оболочки в течение 3 десятилетий, и я никогда не видел этого раньше, я уверен, что я не одинок. Сравните с другими функциями, которые есть на странице bash-hackers, на которую вы ссылались, они все еще есть в руководстве.
2. @Barmar Это упоминается в текущей спецификации POSIX, и, предположительно , некоторые дистрибутивы исправляют справочную страницу bash, чтобы упомянуть об этом. Тем не менее, я подробно остановился на моей «рекомендации» по использованию.
Ответ №2:
$(())
предназначено для арифметических выражений, а расширение фигурных скобок не выполняется в арифметике.
Создайте массив с помощью цикла:
for i in {1..10}
do
vals=()
for j in {1..10}
do
vals =($((i*j)))
done
printf "M" ${vals[@]}
done
Комментарии:
1. Но, с другой стороны, почему расширение фигурных скобок выполняется в
$[]
?2. Это первое, о чем я когда-либо слышал
$[]
. Я не могу найти его в руководстве по bash.3. Это устаревший псевдо-эквивалент
$((
, см. wiki.bash-hackers.org/scripting/obsolete4. Итак, я предполагаю, что технически ваше первое предложение неверно, поскольку якобы
$[]
оно также предназначено для «арифметических выражений».5. Но я думаю, что в то время, когда они его создавали, они не рассматривали арифметическое расширение как совершенно другой контекст со своим собственным синтаксисом.
Ответ №3:
printf M $(({1..10}*i))
не работает из-за порядка, в котором расширяются параметры bash
. Расширение фигурных скобок ( {}
) выполняется раньше, чем арифметическое расширение ( $(())
) by bash
. Ваш код определенно работал бы, если бы все было наоборот.
От man bash
:
Порядок расширений таков: расширение фигурных скобок; расширение тильды, расширение параметров и переменных, арифметическое расширение и подстановка команд (выполняется слева направо); разделение слов; и расширение имени пути.
Ответ №4:
Наткнулся на такой bash. Этот вопрос требует объяснения, а не решения, но здесь был бы $ (()) -способ выразить это.
for i in {1..10}; do
printf M $(eval echo '$(('{1..10}'*i))')
echo
done
Расширение фигурных скобок запрещено внутри арифметического расширения, как и для расширения параметров.
(руководство по bash)
Чтобы избежать конфликтов с расширением параметров, строка ‘${’ не считается подходящей для расширения фигурных скобок и запрещает расширение фигурных скобок до закрывающего ‘}’.