Как перебрать диапазон чисел в Bash, если диапазон задан переменной?
Я знаю, что могу сделать это (это называется «выражение последовательности» в документации Bash ):
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 {01..10}; do echo $i; done
выдаст такие цифры, как01, 02, 03, ..., 10
.myarray=('a' 'b' 'c'); for i in ${!myarray[@]}; do echo $i; done
(обратите внимание на восклицательный знак). Это более конкретный вопрос, чем первоначальный вопрос, но может помочь. Смотрите расширения параметров bash{jpg,png,gif}
которые прямо не рассматриваются, хотя ответ будет идентичным. Видите расширение скобки с переменной? [дубликат], который помечен как дубликат этого.Ответы:
редактировать: я предпочитаю
seq
другие методы, потому что я действительно могу вспомнить это;)источник
seq $END
будет достаточно, поскольку по умолчанию начинается с 1. Fromman seq
: «Если FIRST или INCREMENT опущены, по умолчанию используется значение 1».seq
Метод является самым простым, но Bash имеет встроенную арифметическую оценку.for ((expr1;expr2;expr3));
Конструкция работает так же , какfor (expr1;expr2;expr3)
в С и других подобных языках, как и другие((expr))
случаи, Bash рассматривает их как арифметика.источник
seq
. Используй это!#!/bin/bash
первую строчку своего скрипта. wiki.ubuntu.com/...обсуждение
Использование
seq
хорошо, как предложил Jiaaro. Pax Diablo предложил цикл Bash, чтобы избежать вызова подпроцесса, с дополнительным преимуществом большей дружественности памяти, если $ END слишком велик. Затрус заметил типичную ошибку в реализации цикла, а также намекнул, что, посколькуi
это текстовая переменная, непрерывные преобразования в числа туда-сюда выполняются с соответствующим замедлением.целочисленная арифметика
Это улучшенная версия цикла Bash:
Если единственное, что нам нужно, это то
echo
, что мы могли бы написатьecho $((i++))
.Эфимент научил меня чему-то: Баш позволяет
for ((expr;expr;expr))
конструировать. Так как я никогда не читал всю справочную страницу по Bash (как я делал с справочнойksh
страницей оболочки Korn ( ), и это было давно), я пропустил это.Так,
кажется наиболее эффективным способом использования памяти (нет необходимости выделять память для потребления
seq
выходных данных, что может быть проблемой, если END очень велик), хотя, вероятно, не самый «быстрый».начальный вопрос
Эшерцикл отметил, что нотация { a .. b } Bash работает только с литералами; правда, в соответствии с руководством Bash. Это препятствие можно преодолеть с помощью одного (внутреннего)
fork()
безexec()
(как в случае с вызовомseq
, для которого другое изображение требует fork + exec):Оба
eval
иecho
являются встроенными в Bash, ноfork()
для подстановки команд ($(…)
конструкция) требуется .источник
for ((i=$1;i<=$2;++i)); do echo $i; done
в скрипте у меня отлично работает на bash v.4.1.9, поэтому я не вижу проблемы с аргументами командной строки. Вы имеете в виду что-то еще?time for i in $(seq 100000); do :; done
намного быстрее!Вот почему оригинальное выражение не сработало.
От человека Баш :
Таким образом, раскрытие скобки - это то, что делается вначале как чисто текстовая макрооперация, до раскрытия параметра.
Оболочки - это высоко оптимизированные гибриды между макропроцессорами и более формальными языками программирования. Чтобы оптимизировать типичные варианты использования, язык сделан более сложным, и некоторые ограничения принимаются.
Я бы предложил придерживаться функций Posix 1 . Это означает использование
for i in <list>; do
, если список уже известен, в противном случае используйтеwhile
илиseq
, как в:1. Bash - отличная оболочка, и я использую ее в интерактивном режиме, но я не помещаю bash-isms в свои сценарии. Скриптам может потребоваться более быстрая оболочка, более безопасная, более встроенная. Возможно, им придется работать с тем, что установлено как / bin / sh, и тогда есть все обычные аргументы, соответствующие стандартам. Помните ракушку, она же Башдур?
источник
seq
большими диапазонами. Например,echo {1..1000000} | wc
показывает, что эхо производит 1 строку, миллион слов и 6 888 896 байт. Попыткаseq 1 1000000 | wc
дает миллион строк, миллион слов и 6888896 байт, а также более чем в семь раз быстрее, чем измеряетсяtime
командой.while
Ранее я уже упоминал метод POSIX в своем ответе: stackoverflow.com/a/31365662/895245 Но рад, что вы согласны :-)Способ POSIX
Если вы заботитесь о переносимости, используйте пример из стандарта POSIX :
Вывод:
Вещи, которые не POSIX:
(( ))
без доллара, хотя это общее расширение, упомянутое самим POSIX .[[
,[
здесь достаточно Смотрите также: В чем разница между одинарными и двойными квадратными скобками в Bash?for ((;;))
seq
(GNU Coreutils){start..end}
и это не может работать с переменными, как указано в руководстве по Bash .let i=i+1
: POSIX 7 2. Язык командной оболочки не содержит словаlet
, и он не работает наbash --posix
4.3.42доллар в
i=$i+1
может потребоваться, но я не уверен. POSIX 7 2.6.4 Арифметическое расширение говорит:но, читая его буквально, это не означает, что он
$((x+1))
расширяется, посколькуx+1
не является переменной.источник
x
не ко всему выражению.$((x + 1))
просто отлично.seq
(BSDseq
позволяет устанавливать строку завершения последовательности с помощью-t
), они также имеют версииseq
9.0 и 3.0 соответственно.$((x+1))
и$((x + 1))
синтаксического анализа точно так же, как и когда анализатор размечаетx+1
будет разделен на 3 лексем:x
,+
, и1
.x
не является допустимым числовым токеном, но является допустимым токеном имени переменной, но неx+
является, следовательно, разделением.+
является допустимым токеном арифметического оператора, но+1
его нет, поэтому токен снова разделен там. И так далее.Еще один слой косвенности:
источник
Ты можешь использовать
источник
Если вам нужен префикс, чем это может вам понравиться
это даст
источник
printf "%02d\n" $i
будет проще чемprintf "%2.0d\n" $i |sed "s/ /0/"
?Если вы используете BSD / OS X, вы можете использовать jot вместо seq:
источник
Это прекрасно работает в
bash
:источник
echo $((i++))
работает и объединяет его в одну строку.bash
, мы можем простоwhile [[ i++ -le "$END" ]]; do
сделать (пост-) приращение в тестеЯ объединил несколько идей здесь и измерил производительность.
TL; DR на вынос:
seq
а также{..}
очень быстроfor
а такжеwhile
петли медленные$( )
медленныйfor (( ; ; ))
петли медленнее$(( ))
еще медленнееЭто не выводы . Вам придется взглянуть на код C за каждым из них, чтобы сделать выводы. Это больше о том, как мы склонны использовать каждый из этих механизмов для зацикливания кода. Большинство отдельных операций достаточно близки к той же скорости, что в большинстве случаев не имеет значения. Но такой механизм,
for (( i=1; i<=1000000; i++ ))
как вы можете видеть визуально, - это множество операций. Это также намного больше операций за цикл, чем вы получаетеfor i in $(seq 1 1000000)
. И это может быть неочевидным для вас, поэтому проведение подобных тестов является полезным.демос
источник
$(seq)
это примерно с той же скоростью, что и{a..b}
. Кроме того, каждая операция занимает примерно одно и то же время, поэтому для меня добавляется около 4 мкс к каждой итерации цикла. Здесь операция - это эхо в теле, арифметическое сравнение, приращение и т. Д. Удивительно ли это? Кому интересно, сколько времени понадобится атрибутике цикла, чтобы выполнить свою работу - во время выполнения, скорее всего, будет доминировать содержимое цикла.Я знаю, что этот вопрос о
bash
, но - просто для записи -ksh93
умнее и реализует его, как и ожидалось:источник
Это еще один способ:
источник
Если вы хотите максимально приблизиться к синтаксису выражений фигурных скобок, попробуйте
range
функцию из bash-tricks 'range.bash
.Например, все следующее будет делать то же самое, что и
echo {1..10}
:Он пытается поддерживать собственный синтаксис bash с минимальным количеством возможных ошибок: не только поддерживаются переменные, но
for i in {1..a}; do echo $i; done
также предотвращается часто нежелательное поведение недопустимых диапазонов, предоставляемых в виде строк (например ).Другие ответы будут работать в большинстве случаев, но все они имеют как минимум один из следующих недостатков:
seq
это двоичный файл, который должен быть установлен для использования, должен быть загружен bash и должен содержать ожидаемую программу, чтобы он работал в этом случае. Вездесущий или нет, это гораздо больше, чем просто сам язык Bash.{a..z}
; скобка расширения будет. Вопрос был о диапазонах чисел , так что это обман.{1..10}
синтаксис расширенного диапазона скобок, поэтому программы, которые используют оба, могут быть немного сложнее для чтения.$END
переменная не является допустимым диапазоном «bookend» для другой стороны диапазона. ЕслиEND=a
, например, ошибка не возникнет и дословное значение{1..a}
будет отображено . Это также стандартное поведение Bash, которое часто бывает неожиданным.Отказ от ответственности: я автор связанного кода.
источник
Заменить
{}
на(( ))
:Урожайность:
источник
Все это хорошо, но seq предположительно не рекомендуется и большинство работает только с числовыми диапазонами.
Если вы заключите свой цикл for в двойные кавычки, начальные и конечные переменные будут разыменованы при выводе строки, и вы можете отправить строку обратно в BASH для выполнения.
$i
необходимо экранировать с помощью \ s, чтобы оно НЕ оценивалось перед отправкой в подоболочку.Этот вывод также может быть назначен переменной:
Единственные «издержки», которые это должно генерировать, должны быть вторым экземпляром bash, поэтому он должен подходить для интенсивных операций.
источник
Если вы выполняете команды оболочки и у вас (как и у меня) есть фетиш для конвейерной обработки, это хорошо:
seq 1 $END | xargs -I {} echo {}
источник
Есть много способов сделать это, однако те, которые я предпочитаю, даны ниже
С помощью
seq
Полная команда
seq first incr last
Пример:
Только с первым и последним:
Только с последнего:
С помощью
{first..last..incr}
Здесь первый и последний являются обязательными, а incr является необязательным
Используя только первый и последний
Использование incr
Вы можете использовать это даже для символов, как показано ниже
источник
если вы не хотите использовать '
seq
' или 'eval
'jot
или формат арифметического расширения, например.for ((i=1;i<=END;i++))
или другие петли, например.while
и вы не хотите «printf
и радыecho
» только, тогда этот простой обходной путь может соответствовать вашему бюджету:a=1; b=5; d='for i in {'$a'..'$b'}; do echo -n "$i"; done;' echo "$d" | bash
PS: Мой bash не имеет команды '
seq
' в любом случае.Протестировано на Mac OSX 10.6.8, Bash 3.2.48
источник
Это работает в Баш и Корне, также может идти от более высоких к более низким числам. Вероятно, не самый быстрый или самый красивый, но работает достаточно хорошо. Обрабатывает негативы тоже.
источник