Расширение скобы с переменной? - программирование
Подтвердить что ты не робот

Расширение скобы с переменной?

#!/bin/sh
for i in {1..5}
do
   echo "Welcome"
done

Будет работать, отображает приветствие 5 раз.

#!/bin/sh
howmany=`grep -c $1 /root/file`
for i in {1..$howmany}
do
   echo "Welcome"
done

Не работает! howmany будет равным 5, так как будет отображаться вывод grep -c. $1 - параметр 1, который специфичен при запуске script.

Любые идеи?

4b9b3361

Ответ 1

создать последовательность для управления петлей

for i in $(seq 1 $howmany); do
echo "Welcome";
done

Ответ 2

Расширение скобки вычисляется до того, как переменные будут расширены. Вместо этого вам нужен c-стиль для цикла:

for ((i=1;i<=howmany;i++))
do
   echo "Welcome"
done

Ответ 3

Обходные пути для невозможности использования переменных в выражении скобок последовательности:

  • Если целью является просто перебрать номера в диапазоне - как в случае OP - лучший не использовать расширение скобок, но вместо этого используйте bash цикл C-стиля - см. user000001 answer.

    • Если конкретные числа не важны, и вам просто нужно выполнить тело цикла определенное количество раз, Ответ Коула Тирни является опцией.
  • Если использование расширения скобки желательно, тем не менее:

    • Если вам НЕ нужны числа в списке, чтобы иметь префикс или постфикс, используйте утилиту seq с заменой без кавычек (небольшая caveat: seq НЕ является утилитой POSIX, но она широко доступна); например.

      • echo $(seq 3)1 2 3; начальный номер 1 подразумевается
        • echo $(seq -f '%02.f' 3)01 02 03 - ноль-padded
      • echo $(seq 2 4)2 3 4; явные начальные и конечные номера
      • echo $(seq 1 2 5)1 3 5; пользовательский прирост (2 посередине)
    • Если вы нужны номера в списке, чтобы иметь префикс или постфикс, у вас есть несколько вариантов:

      • Используйте утилиту seq с ее опцией -f для предоставления строки формата printf-стиля (как указано выше для нулевого заполнения) или чистых bash обходных путей на основе eval (требуется дополнительная помощь!) или создание массива в цикле, все из которых подробно описаны в этом ответе.
      • Вы также можете рассмотреть возможность реализации функций в целом, например, путем написания пользовательской функции оболочки или пользовательского script с помощью утилит, таких как awk или perl.

Пример безопасного использования eval с переменными, управляющими выражением скобки последовательности:

Переменные проверяются заранее, чтобы убедиться, что они содержат десятичные целые числа.

from=1 to=3  # sample values

# Ensure that $from and $to are decimal numbers and abort, if they are not.
(( 10#$from + 10#$to || 1 )) 2>/dev/null || { echo "Need decimal integers" >&2; exit 1; }

eval echo "A{$from..$to}"  # -> 'A1 A2 A3'

Общий обзор расширения брекетов

Основной целью расширения является развернуть список токенов с каждым токеном, имеющим опциональный префикс и/или постфикс; разложение фигурных скобок должно быть неуказанным и прийти в 2 аромата:

  • a фиксированная серия (список) разделенных запятыми строк - поддерживаемые переменные
    • указывает и расширяет фиксированное количество токенов (2 или более); например:.
    • echo A{b,c,d}Ab Ac Ad, т.е. 3 токена, что подразумевается количеством аргументов.
    • echo {/,$HOME/}Library например, → /Library /User/jdoe/Library
    • Поддерживаются переменные ссылки и даже глобусы, но имейте в виду, что они расширяются после расширения брекета в его результате при обычной оценке.
  • a выражение последовательности (диапазон) с .., обычно числовые - переменные НЕ поддерживаются

    • расширяет до число переменных токенов, управляемых буквальными начальными и конечными точками (для исторических причин, использование переменных НЕ поддерживается - см. комментарии user000001 answer):
      • [редкость] строки: допускаются только отдельные английские буквы; например {a..c}
      • числа: только десятичные целые числа; например, {1..10}, {10..1}, {-1..2}
        • пример с префиксом и постфикс: A{1..3}#A1# A2# A3#
        • сломанный пример с переменными: {$from..$to} # !! FAILS - $from и $to интерпретируются как литералы и поэтому не распознаются ни одной буквой, ни десятичным целым - не выполняется расширение брекетинга (см. ниже).
          • напротив, с использованием переменных работает в zsh и ksh.
      • bash 4 + добавляет две функции:
        • необязательный шаг шага приращения:
          • echo A{1..5..2}A1 A3 A5 - числа, увеличивающиеся на 2
        • способность нулевой панели:
          • echo A{001..003}A001 A002 A003
  • недопустимое выражение не расширено (рассматривается как обычная строка без кавычек, с { и } рассматривается как литералы):

    • echo {}'{}' - недействителен в качестве фигурной скобки: не менее 2 , - необходимы разделенные токены
      • Это позволяет использовать, например, некорректный {} с find.
    • echo {1..$to}'{1..<value-of-$to>}' - недействителен как выражение привязки. in bash: переменные не поддерживаются; однако, действительны в ksh и zsh.
    • (fish, напротив, расширяет последовательность {...}, аналогично, zsh имеет параметр BRACE_CCL (по умолчанию отключен) для расширения отдельных символов внутри {..}, что эффективно вызывает расширение любого непустого {...}.)

Ответ 4

Проблема заключается в том, что "расширение скобки" выполняется перед "переменным расширением"

for i in $(seq 1 $howmany) 

работает, как сказал @damienfrancois, или, если хотите:

for i in $(eval echo '{$start..10}') 

возможно, но не использует его для всех здравомыслия.

Ответ 5

Вы также можете использовать цикл while:

while ((howmany--)); do
   echo "Welcome"
done

Ответ 6

В этом случае мы могли бы использовать eval:

howmany=`grep -c $1 /root/file`
for i in $(eval echo {1..$howmany}); do
    echo "Welcome"
done