Как выполнить итерацию по диапазону чисел в 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}
for i in $(seq 1 $END); do echo $i; done
edit: Я предпочитаю seq
другим методам, потому что я действительно могу его запомнить ;)
В след
способ является самым простым, но в bash есть встроенная арифметической оценки.
END=5
for ((i=1;i<=END;i++)); do
echo $i
done
# ==> outputs 1 2 3 4 5 on separate lines
Для ((выражение1;выражение2;выражение3));построить работает так же, как для (выражение1;выражение2;выражение3)
в C-подобных языков, и, как и другие `((выражение)), потому что, Баш рассматривает их как арифметика.
Используя сл
хорошо, как Jiaaro предложил. Пакс Диабло предложил цикл в bash, чтобы избежать вызова подпроцесса, с дополнительным преимуществом, что больше памяти-фрэндли если $конец слишком большие. Zathrus замечена типичная ошибка в реализации цикла, а также намекнул, что, поскольку " я " является текстовой переменной, непрерывных преобразований к-и-FRO цифры выполняются с соответствующим замедлением.
Это улучшенная версия цикла Баш:
typeset -i i END
let END=5 i=1
while ((i<=END)); do
echo $i
…
let i++
done
Если единственное, что мы хотим-это "эхо", то мы могли бы написать Эхо $((я++))
.
ephemient научило меня кое-чему: в Bash для ((выражение;выражение;выражение))
конструктов. Так как я'ве никогда не прочитать Man-страницу для bash (как я'ве сделали с Korn-оболочку (КШ
) Man-странице, и это было давно), я пропустил.
Так,
typeset -i i END # Let's be explicit
for ((i=1;i<=END;++i)); do echo $i; done
как представляется, наиболее эффективный для памяти способ (он выиграл'т быть надо выделить память, чтобы потреблять сл
'ы выходной, который может быть проблемой, если конец очень большие), хотя, наверное, не самый “быстрый”.
eschercycle отметить, что {a..печатать} нотации Баш работает только с литералами; верно, в соответствии с Баш инструкцию. Можно преодолеть это препятствие с одной (внутренней) вызов Fork()
без метод exec()
(как в случае с вызовом след
, который будучи еще изображения, требует вилка+exec будет):
for i in $(eval echo "{1..$END}"); do
Обе функции "eval" и " эхо " - это bash примитивы, а вызов Fork()требуется для подстановки команды (в
$(...)` строительство).
Вот почему исходное выражение Не'т работу.
Из человек Баш:
расширение бандажа производится до любые другие расширения, и любой специальные символы на другие расширения сохраняются в В результате. Это строго текстуальное. Баш не применять каких-либо синтаксических перевод в контексте расширение или текст между подтяжки.
Так, расширение скобок что-то рано как чисто текстовый макрос операции, прежде чем параметр расширения.
Снаряды высоко оптимизированный гибридов между макро-процессоров и более формальных языков программирования. Для того, чтобы оптимизировать типичные случаи использования, язык сделан более сложные и приняты некоторые ограничения.
рекомендации
Я хотел бы предложить придерживаться стандарта 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 и ГТ;
Пути в POSIX
Если вы заботитесь о переносимости, используйте пример из стандарта POSIX:
i=2
end=5
while [ $i -le $end ]; do
echo $i
i=$(($i+1))
done
Выход:
2
3
4
5
Вещей, которые не в POSIX:
(( ))
без доллара, хотя это обычное расширение как упомянуто на себя в POSIX.[[
. [
достаточно здесь. См. также: https://stackoverflow.com/questions/13542832/bash-if-difference-between-square-brackets-and-double-square-bracketsслед
(Лицензия GNU Coreutils){начало..конец}
, и что не может работать с переменными, как сказано в руководство bash.: [в POSIX 7 2. Язык командной оболочки](http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_04) не содержит слово
да, и он не на гулянки ... в POSIX
4.3.42если оболочка переменная X содержит значение, которое образует число постоянное, при необходимости, в том числе ведущим знаком плюс или минус, то арифметические разложения " и$((Х))" и "$(($Х))", которая должна возвращать одинаковое значение.
но значение это буквально это не означает, что $((Х+1))
расширяется с х+1
- это не переменная.
Вы можете использовать
for i in $(seq $END); do echo $i; done
Если вы'вновь на БСД / ОС Х можно использовать Йота вместо сл:
for i in $(jot $END); do echo $i; done
Я'вэ комбинировать несколько идей здесь и измеренные характеристики.
след
и {..}
очень быстроза
и время
петли медленно$( )
это медленноЭто не выводы. Вы должны посмотреть на код 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<=1000000; i++ )) ; do echo $i; done | wc)
1000000 1000000 6888896
real 0m12.391s
user 0m11.069s
sys 0m3.437s
# show that arithmetic expansion is even slower
$ time (i=1; e=1000000; while [ $i -le $e ]; do echo $i; i=$(($i+1)); done | wc)
1000000 1000000 6888896
real 0m19.696s
user 0m18.017s
sys 0m3.806s
$ time (i=1; e=1000000; while [ $i -le $e ]; do echo $i; ((i=i+1)); done | wc)
1000000 1000000 6888896
real 0m18.629s
user 0m16.843s
sys 0m3.936s
$ time (i=1; e=1000000; while [ $i -le $e ]; do echo $((i++)); done | wc)
1000000 1000000 6888896
real 0m17.012s
user 0m15.319s
sys 0m3.906s
# even a noop is slow
$ time (i=1; e=1000000; while [ $((i++)) -le $e ]; do :; done | wc)
0 0 0
real 0m12.679s
user 0m11.658s
sys 0m1.004s
Я знаю, что этот вопрос о Баш
, но - только на запись - 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}
Если вы хотите, чтобы остаться как можно ближе к раскосу-выражения, опробовать функции спектр
от Баш-трюки' диапазон.Баш
.
Например, все следующие будут делать то же самое, как эхо {1..10}
:
source range.bash
one=1
ten=10
range {$one..$ten}
range $one $ten
range {1..$ten}
range {1..10}
Он пытается поддержать родной Баш синтаксис С и"подводных камней" и как это возможно: не только переменные поддерживаются, но часто нежелательное поведение недопустимых диапазонов поступает в виде строк (например, для меня в {1..а}; сделать эхо $я; сделано`) предотвращается также.
Остальные ответы будут работать в большинстве случаев, но все они имеют по крайней мере один из следующих недостатков:
след
- это двоичный файл, который должен быть установлен, чтобы использоваться, должны быть загружены на баш, и должен содержать программу вы ожидаете, для того, чтобы работать в этом случае. Вездесущий или нет, это's большое больше полагаться на себя, чем просто на языке bash.{а..з}
; фигурные скобки будет. Вопрос был про диапазоны numbers, хотя, так что это придирка.{1..10}
скобки-расширенный синтаксис, так что программы, использующие оба могут быть немного труднее читать.$конец
не является допустимым и quot диапазона;подставки для книг" и на другой стороне диапазона. If конец=а
, например, ошибки не произойдет и буквальное значение {1..а}
будет вторит. Это поведение по умолчанию bash, а так-это просто зачастую неожиданные.Отказ от ответственности: я автор связан код.
Заменить {}
с (( ))
:
tmpstart=0;
tmpend=4;
for (( i=$tmpstart; i<=$tmpend; i++ )) ; do
echo $i ;
done
Дает:
0
1
2
3
4
Это все хорошо, но сл якобы устарела и наиболее работать только с числовыми диапазонами.
Если вы заключите свой цикл в двойные кавычки, время начала и окончания переменные будут разыменованы, когда вы повторить строку, и вы можете отправить строку обратно в 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`
Единственный на "накладные расходы" и это должно генерировать должен быть второй экземпляр Баш, так что должны быть пригодны для интенсивной эксплуатации.
Если вы'снова делаешь скрипт и вы (как и я) имеют фетиш для конвейеризации, это хорошо:
сл 1 $конец | команды xargs -я {} Эхо {}
Есть много способов сделать это, однако я предпочитаю приведен ниже
след
синопсис с человеком сл`
$ seq [-w] [-f format] [-s string] [-t string] [first [incr]] last
синтаксис
Полная команда<БР>
сл первых не в прошлом
Пример:
$ 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
Это работает в 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
elif (( ${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"
}
если вы Don'т хотите использовать 'сл
' или 'ивал
' или Йота
или арифметика расширение формата например, для ((Я=1;я&л;=конца;я++))
, или другие циклы, например, А
, и вы Дон'т хочу 'е
' и с удовольствием 'Эхо
' только, тогда этот простой метод может соответствовать вашему бюджету:
а=1; Б=5; д='я в {' долларов'..'$б'}; делать Эхо -Н " и я" у; сделано;' Эхо " Д"был$; | Баш
PS: мой Баш не'т 'сл
' команда в любом случае.
Протестирован на Mac и OSX 10.6.8, Баш 3.2.48