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

119

У меня есть следующее:

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?
  • 11
    FreeBSD нет.
Показать ещё 4 комментария
Теги:
syntax

10 ответов

154
Лучший ответ

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

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

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

max=10
for i in `seq 2 $max`
do
    echo "$i"
done
  • 0
    Нет веской причины использовать внешнюю команду, такую как seq для подсчета и приращения чисел в цикле for, поэтому рекомендуется избегать использования seq . Эта команда оставлена для совместимости со старым bash. Встроенные команды достаточно быстрые. for (( EXP1; EXP2; EXP3 )) ...
  • 9
    @miller синтаксис for (( ... )) - это не POSIX, и это означает, например, что он не будет работать из коробки на вещах, подобных Alpine Linux. seq делает.
Показать ещё 1 комментарий
43

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

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

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

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

Настройте цикл вручную:

i=0
max=10
while [ $i -lt $max ]
do
    echo "output: $i"
    true $(( i++ ))
done

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

max=10
for (( i=0; i &lt max; i++ )); do echo "output: $i"; done

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

for i in $( jot 0 10 ); do echo "output: $i"; done
  • 1
    +1 за jot, который должен быть доступен в MacOS: developer.apple.com/mac/library/documentation/Darwin/Reference/…
  • 3
    true $(( i++ )) работает не во всех случаях, поэтому наиболее переносимым будет true $((i=i+1)) .
Показать ещё 1 комментарий
7

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

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

ИЗМЕНИТЬ: Нет seq? Тогда как насчет бедного человека seq с perl?

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

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

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

Здесь более одного способа сделать это.

max=10
for i in `eval "echo {2..$max}"`
do
    echo "$i"
done
  • 7
    Риск безопасности, поскольку eval будет оценивать все, что вы установили для max . Рассмотрим max="2}; echo ha; #" , затем замените echo ha чем-то более разрушительным.
  • 4
    (S) он установил максимум на 10. Без риска.
2

Это способ:
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.

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

Здесь он работал на MAC-OS // Он включает пример даты BSD, как увеличивать и уменьшать дату также

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

как насчет

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

Вам нужен явный вызов eval для переоценки {} после замены переменной

0

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

max=5
i=1

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

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

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню