Сценарий цикла «для» сценария оболочки


191

Я получил следующее на работу:

for i in {2..10}
do
    echo "output: $i"
done

Он производит кучу строк output: 2, output: 3и так далее.

Тем не менее, пытаясь запустить следующее:

max=10
for i in {2..$max}
do
    echo "$i"
done

производит следующее:

output: {2..10}

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


2
какую систему и оболочку вы используете? В какой тупой системе есть sh или bash, но нет seq, coreutil?
whatsisname

11
FreeBSD нет.
Ницше Жу

echo "$iдолжно быть echo "$i"- не решит проблему, хотя.
Дэйв Джарвис

Малый стиль нит: Я обычно вижу doи thenключевые слова на той же линии , как forи if, соответственно. Например,for i in {2..10}; do
платный ботаник

Ответы:


234

Раскрытие скобок , {x..y} выполняется перед другими раскрытиями, поэтому вы не можете использовать это для последовательностей переменной длины.

Вместо этого используйте seq 2 $maxметод, как указано пользователем mob .

Итак, для вашего примера это будет:

max=10
for i in `seq 2 $max`
do
    echo "$i"
done

Нет веской причины использовать внешнюю команду, например, seqдля подсчета и приращения чисел в цикле for, поэтому рекомендуется избегать ее использования seq. Эта команда оставлена ​​для совместимости со старым bash. Встроенные команды достаточно быстрые. for (( EXP1; EXP2; EXP3 )) ...
Миллер

13
@miller for (( ... ))синтаксис не POSIX, и это означает, например, что он не будет работать из коробки на вещах, таких как Alpine Linux. seqделает.
Вечерняя ночь

@Wernight Не только Alpine Linux; #!/bin/shэто Dash в Debian / Ubuntu.
Франклин Ю,

72

Попробуйте версию с арифметическим выражением for:

max=10
for (( i=2; i <= $max; ++i ))
do
    echo "$i"
done

Это доступно в большинстве версий bash и должно быть совместимо с Bourne shell (sh).


1
@Flow: Хм, я только что попробовал это на паре систем (на основе Linux и BSD) с #! / Bin / sh, и все заработало нормально. Вызывается в bash и, в частности, в / bin / sh, все еще работает. Может быть, версия sh имеет значение? Вы на каком-то старом Unix?
система PAUSE

8
Очень немногие системы имеют выделенную sh, вместо этого делая ее ссылкой на другую оболочку. В идеале, такая оболочка вызывается как shбы только поддерживать эти функции в стандарте POSIX, но по умолчанию пусть некоторые из своих дополнительных функций через. Цикл for в стиле C не является функцией POSIX, но может быть в shрежиме реальной оболочки.
Чепнер

27

Шаг цикла вручную:

I = 0
макс = 10
в то время как [$ i -lt $ max]
делать
    echo "output: $ i"
    true $ ((i ++))
сделано

Если вам не нужно быть полностью POSIX, вы можете использовать арифметику для цикла:

макс = 10
для ((i = 0; i <max; i ++)); сделайте эхо "output: $ i"; сделано

Или используйте jot (1) в системах BSD:

для меня в $ (Jot 0 10); сделайте эхо "output: $ i"; сделано

3
true $(( i++ ))не работает во всех случаях, так что большинство портативных будет true $((i=i+1)).
Flow

точка с запятой не должна входить в "for ((i = 0; i <max; i ++));"
Логан

9

Есть несколько способов сделать это.

max=10
for i in `eval "echo {2..$max}"`
do
    echo "$i"
done

7
Риск безопасности, так evalкак оценит все, что вы установили max. Подумайте max="2}; echo ha; #", затем замените echo haчем-нибудь более разрушительным.
chepner

6
(S) он установил максимум на 10. Без риска.
Конрад Мейер

9

Если seqкоманда доступна в вашей системе:

for i in `seq 2 $max`
do
  echo "output: $i"
done

Если нет, то используйте бедняков seqс perl:

seq=`perl -e "\$,=' ';print 2..$max"`
for i in $seq
do
  echo "output: $i"
done

Смотреть эти кавычки.


Я только что проверил это; не кажется, что это так.
эйканал

5

Это способ:
Bash:

max=10
for i in $(bash -c "echo {2..${max}}"); do echo $i; done

Выше Bash способ будет работать kshи zshтоже, когда bash -cзаменяется ksh -cили zsh -cсоответственно.

Примечание: for i in {2..${max}}; do echo $i; doneработает в zshа ksh.


4

Мы можем повторять цикл, как в C-программировании.

#!/bin/bash
for ((i=1; i<=20; i=i+1))
do 
      echo $i
done

2

Здесь это работало на Mac OS X.

Он включает в себя пример даты BSD, как увеличить и уменьшить дату также:

for ((i=28; i>=6 ; i--));
do
    dat=`date -v-${i}d -j "+%Y%m%d"` 
    echo $dat
done

2

Ну, поскольку у меня не было seqкоманды, установленной в моей системе (Mac OS X v10.6.1 (Snow Leopard)), я whileвместо этого использовал цикл:

max=5
i=1

while [ $max -gt $i ]
do
    (stuff)
done

* Пожимает плечами * все, что работает.


2
Seq является относительно новым. Я узнал об этом только несколько месяцев назад. Но вы можете использовать цикл for! Недостаток 'while' заключается в том, что вы должны помнить, что нужно увеличить счетчик где-нибудь внутри цикла, иначе цикл будет уменьшен.
система PAUSE

1

Все они делают {1..8}и должны быть POSIX. Они также не сломаются, если вы поместите условие continueв цикл. Канонический способ:

f=
while [ $((f+=1)) -le 8 ]
do
  echo $f
done

По-другому:

g=
while
  g=${g}1
  [ ${#g} -le 8 ]
do
  echo ${#g}
done

и другой:

set --
while
  set $* .
  [ ${#} -le 8 ]
do
  echo ${#}
done

1

Использование:

max=10
for i in `eval echo {2..$max}`
do
    echo $i
done

Вам нужен явный вызов 'eval', чтобы переоценить {} после подстановки переменной.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.