Как выполнить итерацию по диапазону чисел, заданных переменными в Bash?

Как выполнить итерацию по диапазону чисел в Bash, когда диапазон задается переменной?

Я знаю, что могу это сделать (это называется "выражение последовательности" в [документации] Bash1):

 for i in {1..5}; do echo $i; done

Что дает:

1
2
3
4
5

И все же, как я могу заменить любую из конечных точек диапазона переменной? Это не работает:

END=5
for i in {1..$END}; do echo $i; done

Что печатает:

{1..5}

Комментарии к вопросу (5)
Решение
for i in $(seq 1 $END); do echo $i; done

edit: Я предпочитаю seq другим методам, потому что я действительно могу его запомнить ;)

Комментарии (11)

В след способ является самым простым, но в bash есть встроенная арифметической оценки.

END=5
for ((i=1;i outputs 1 2 3 4 5 on separate lines

Для ((выражение1;выражение2;выражение3));построить работает так же, как для (выражение1;выражение2;выражение3) в C-подобных языков, и, как и другие `((выражение)), потому что, Баш рассматривает их как арифметика.

Комментарии (10)

обсуждение

Используя сл хорошо, как Jiaaro предложил. Пакс Диабло предложил цикл в bash, чтобы избежать вызова подпроцесса, с дополнительным преимуществом, что больше памяти-фрэндли если $конец слишком большие. Zathrus замечена типичная ошибка в реализации цикла, а также намекнул, что, поскольку " я " является текстовой переменной, непрерывных преобразований к-и-FRO цифры выполняются с соответствующим замедлением.

целочисленной арифметики

Это улучшенная версия цикла Баш:


typeset -i i END
let END=5 i=1
while ((i
Комментарии (5)

Вот почему исходное выражение Не'т работу.

Из человек Баш:

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

Так, расширение скобок что-то рано как чисто текстовый макрос операции, прежде чем параметр расширения.

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

рекомендации

Я хотел бы предложить придерживаться стандарта POSIX<суп>1</SUP и> характеристики. Это означает, используя для Я в в <список> У; У, если список уже известен, в противном случае, использоватьАилислед`, как в:

#!/bin/sh

limit=4

i=1; while [ $i -le $limit ]; do
  echo $i
  i=$(($i + 1))
done
# Or -----------------------
for i in $(seq 1 $limit); do
  echo $i
done

<ч> <суп>1. Баш-это отличный снаряд, и я использую его в интерактивном режиме, но я не'т положить в bash-измы в моем сценарии. Скриптов может понадобиться быстрее оболочки, более защищенными, более встроенный-стиль один. Они, возможно, потребуется что-то устанавливается как /bin/sh, а затем есть все обычные про-стандарты рассуждения. Помните контузия, ака bashdoor? в </SUP и ГТ;

Комментарии (4)

Пути в POSIX

Если вы заботитесь о переносимости, используйте пример из стандарта POSIX:

i=2
end=5
while [ $i -le $end ]; do
    echo $i
    i=$(($i+1))
done

Выход:

2
3
4
5

Вещей, которые не в POSIX:

если оболочка переменная X содержит значение, которое образует число постоянное, при необходимости, в том числе ведущим знаком плюс или минус, то арифметические разложения " и$((Х))" и "$(($Х))", которая должна возвращать одинаковое значение.

но значение это буквально это не означает, что $((Х+1)) расширяется с х+1 - это не переменная.

Комментарии (5)

Еще один уровень косвенности:

for i in $(eval echo {1..$END}); do
    ∶
Комментарии (1)

Вы можете использовать

for i in $(seq $END); do echo $i; done
Комментарии (8)

Если вам нужен префикс, чем вы могли бы это


 for ((i=7;i
Комментарии (1)

Если вы'вновь на БСД / ОС Х можно использовать Йота вместо сл:

for i in $(jot $END); do echo $i; done
Комментарии (2)

Это прекрасно работает в bash:

END=5
i=1 ; while [[ $i -le $END ]] ; do
    echo $i
    ((i = i + 1))
done
Комментарии (7)

Я'вэ комбинировать несколько идей здесь и измеренные характеристики.

Многабукаф. на вынос:

  1. след и {..} очень быстро
  2. за и время петли медленно
  3. $( ) это медленно
  4. на (( ; ; ))` петли медленнее
  5. ` $ (( ))- это даже медленнее
  6. Беспокоясь о н номера в памяти телефона (сл или { .. }) - это глупо (по крайней мере до 1 миллиона.)

Это не выводы. Вы должны посмотреть на код C за каждым из них, чтобы делать выводы. Это больше о том, как мы склонны использовать каждый из этих механизмов перебором кода. Большинство отдельных операций достаточно близко, чтобы быть той же скоростью, что он's не имеет значения в большинстве случаев. Но механизм, как на (( Я=1; я&л;=1000000; я++ )) - это множество операций, как вы можете наглядно увидеть. Мы также многие другие операции в петле, чем вы получаете из Для меня в $(сл 1 1000000)`. И что не может быть очевидным для вас, поэтому делаю тесты, как это ценно.

Демос


# show that seq is fast
$ time (seq 1 1000000 | wc)
 1000000 1000000 6888894

real    0m0.227s
user    0m0.239s
sys     0m0.008s

# show that {..} is fast
$ time (echo {1..1000000} | wc)
       1 1000000 6888896

real    0m1.778s
user    0m1.735s
sys     0m0.072s

# Show that for loops (even with a : noop) are slow
$ time (for i in {1..1000000} ; do :; done | wc)
       0       0       0

real    0m3.642s
user    0m3.582s
sys 0m0.057s

# show that echo is slow
$ time (for i in {1..1000000} ; do echo $i; done | wc)
 1000000 1000000 6888896

real    0m7.480s
user    0m6.803s
sys     0m2.580s

$ time (for i in $(seq 1 1000000) ; do echo $i; done | wc)
 1000000 1000000 6888894

real    0m7.029s
user    0m6.335s
sys     0m2.666s

# show that C-style for loops are slower
$ time (for (( i=1; i
Комментарии (2)

Я знаю, что этот вопрос о Баш, но - только на запись - ksh93 умнее и реализует его, как ожидалось:

$ ksh -c 'i=5; for x in {1..$i}; do echo "$x"; done'
1
2
3
4
5
$ ksh -c 'echo $KSH_VERSION'
Version JM 93u+ 2012-02-29

$ bash -c 'i=5; for x in {1..$i}; do echo "$x"; done'
{1..5}
Комментарии (0)

Если вы хотите, чтобы остаться как можно ближе к раскосу-выражения, опробовать функции спектр от Баш-трюки' диапазон.Баш.

Например, все следующие будут делать то же самое, как эхо {1..10}:

source range.bash
one=1
ten=10

range {$one..$ten}
range $one $ten
range {1..$ten}
range {1..10}

Он пытается поддержать родной Баш синтаксис С и"подводных камней" и как это возможно: не только переменные поддерживаются, но часто нежелательное поведение недопустимых диапазонов поступает в виде строк (например, для меня в {1..а}; сделать эхо $я; сделано`) предотвращается также.

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

  • Многие из них используют подоболочек, которые могут не и не может быть possible на некоторых системах.
  • Многие из них полагаются на внешние программы. Даже след - это двоичный файл, который должен быть установлен, чтобы использоваться, должны быть загружены на баш, и должен содержать программу вы ожидаете, для того, чтобы работать в этом случае. Вездесущий или нет, это's большое больше полагаться на себя, чем просто на языке bash.
  • Решения, которые используют только собственные функции Баш, как @ephemient'ы, не будет работать на алфавитных диапазонов, как {а..з}; фигурные скобки будет. Вопрос был про диапазоны numbers, хотя, так что это придирка.
  • Большинство из них'т визуально похожие на {1..10} скобки-расширенный синтаксис, так что программы, использующие оба могут быть немного труднее читать.
  • @bobbogo'ы ответьте использует некоторые из знакомых синтаксис, но делает что-то неожиданное, если переменная $конец не является допустимым и quot диапазона;подставки для книг" и на другой стороне диапазона. If конец=а, например, ошибки не произойдет и буквальное значение {1..а} будет вторит. Это поведение по умолчанию bash, а так-это просто зачастую неожиданные.

Отказ от ответственности: я автор связан код.

Комментарии (0)

Это еще один способ:

end=5
for i in $(bash -c "echo {1..${end}}"); do echo $i; done
Комментарии (2)

Заменить {} с (( )):


tmpstart=0;
tmpend=4;

for (( i=$tmpstart; i
Комментарии (1)

Это все хорошо, но сл якобы устарела и наиболее работать только с числовыми диапазонами.

Если вы заключите свой цикл в двойные кавычки, время начала и окончания переменные будут разыменованы, когда вы повторить строку, и вы можете отправить строку обратно в Bash для выполнения. `$я должен быть экранирован с \'s так это не оценивали перед отправкой в подоболочку.

RANGE_START=a
RANGE_END=z
echo -e "for i in {$RANGE_START..$RANGE_END}; do echo \\${i}; done" | bash

Этот выход может быть присвоено переменной:

VAR=`echo -e "for i in {$RANGE_START..$RANGE_END}; do echo \\${i}; done" | bash`

Единственный на "накладные расходы" и это должно генерировать должен быть второй экземпляр Баш, так что должны быть пригодны для интенсивной эксплуатации.

Комментарии (0)

Если вы'снова делаешь скрипт и вы (как и я) имеют фетиш для конвейеризации, это хорошо:

сл 1 $конец | команды xargs -я {} Эхо {}

Комментарии (0)

Есть много способов сделать это, однако я предпочитаю приведен ниже

Используя след

синопсис с человеком сл`

$ seq [-w] [-f format] [-s string] [-t string] [first [incr]] last

синтаксис

Полная команда<БР> сл первых не в прошлом

  • первый-начальный номер в последовательности [опционально, по умолчанию:1]
  • пов-это приращение [опционально, по умолчанию:1]
  • последнее-последнее число в последовательности

Пример:

$ seq 1 2 10
1 3 5 7 9

Только с первой и последней:

$ seq 1 5
1 2 3 4 5

Только с последним:

$ seq 5
1 2 3 4 5

С помощью {первый..последний..инкр}

Здесь первый и последний являются обязательными и не является обязательным

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

$ echo {1..5}
1 2 3 4 5

Используя инкр

$ echo {1..10..2}
1 3 5 7 9

Вы можете использовать это даже для символов, как показано ниже

$ echo {a..z}
a b c d e f g h i j k l m n o p q r s t u v w x y z
Комментарии (0)

Это работает в bash и Korn, также может идти от высших до низших чисел. Наверное, не самый быстрый или красивый, но работает достаточно хорошо. Тоже ручки негативы.

function num_range {
   # Return a range of whole numbers from beginning value to ending value.
   # >>> num_range start end
   # start: Whole number to start with.
   # end: Whole number to end with.
   typeset s e v
   s=${1}
   e=${2}
   if (( ${e} >= ${s} )); then
      v=${s}
      while (( ${v} = ${e} )); do
         echo ${v}
         ((v=v-1))
      done
   fi
}

function test_num_range {
   num_range 1 3 | egrep "1|2|3" | assert_lc 3
   num_range 1 3 | head -1 | assert_eq 1
   num_range -1 1 | head -1 | assert_eq "-1"
   num_range 3 1 | egrep "1|2|3" | assert_lc 3
   num_range 3 1 | head -1 | assert_eq 3
   num_range 1 -1 | tail -1 | assert_eq "-1"
}
Комментарии (0)

если вы Don'т хотите использовать 'сл' или 'ивал' или Йота или арифметика расширение формата например, для ((Я=1;я&л;=конца;я++)), или другие циклы, например, А, и вы Дон'т хочу 'е' и с удовольствием 'Эхо' только, тогда этот простой метод может соответствовать вашему бюджету:

а=1; Б=5; д=&#39;я в {&#39; долларов&#39;..&#39;$б&#39;}; делать Эхо -Н " и я" у; сделано;&#39; Эхо " Д&quotбыл$; | Баш

PS: мой Баш не'т 'сл' команда в любом случае.

Протестирован на Mac и OSX 10.6.8, Баш 3.2.48

Комментарии (0)