Как я могу создать арифметический цикл в сценарии оболочки POSIX?

12

Я знаю, как создать арифметический forцикл в bash.

Как сделать эквивалентный цикл в сценарии оболочки POSIX?

Поскольку существуют разные способы достижения одной и той же цели, не стесняйтесь добавлять свой собственный ответ и немного рассказать о том, как он работает.

Ниже приведен пример одного такого bashцикла:

#!/bin/bash
for (( i=1; i != 10; i++ ))
do
    echo "$i"
done
LinuxSecurityFreak
источник
@ StéphaneChazelas, потому что это была заметка об истории, которая, казалось, была основана на недоразумении, так как OP не предполагал, что это была вещь bash, а просто использовала bash в качестве примера. Это действительно не казалось актуальным.
Terdon

Ответы:

13

Я нашел полезную информацию в вики Shellcheck.net , цитирую:

  1. Bash:

    for ((init; test; next)); do foo; done
  2. POSIX:

    : "$((init))"
    while [ "$((test))" -ne 0 ]; do foo; : "$((next))"; done
    

хотя будьте осторожны, i++это не POSIX, поэтому придется переводить, например, в i += 1или i = i + 1.


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

#!/bin/sh
: "$((i=1))"
while [ "$((i != 0))" -ne 0 ]
do
    echo "$i"
    : "$((i = i + 1))"
done

Хотя здесь вы можете сделать его более разборчивым с помощью:

#!/bin/sh
i=1
while [ "$i" -ne 10 ]
do
    echo "$i"
    i=$((i + 1))
done

Например init, мы присваиваем постоянное значение, поэтому нам не нужно вычислять арифметическое выражение. Значение i != 10in testможет быть легко преобразовано в [выражение, а nextиспользование присваивания переменной оболочки в отличие от присваивания переменной внутри арифметического выражения позволяет избавиться от :необходимости заключать в кавычки.


Помимо i++-> i = i + 1, есть больше переводов конструкций, специфичных для ksh / bash, которые не являются POSIX, которые вам, возможно, придется сделать:

  • i=1, j=2, Арифметический оператор не очень POSIX (и конфликты с десятичным разделителем в некоторых местах с ksh93). Вы могли бы заменить его другим оператором, как в, но использование было бы намного более разборчивым.,+: "$(((i=1) + (j=2)))"i=1 j=2
  • a[0]=1: нет массивов в оболочках POSIX
  • i = 2**20: нет оператора питания в синтаксисе оболочки POSIX. <<хотя поддерживается для степеней два, можно использовать i = 1 << 20. Для других полномочий можно прибегнуть к bc:i=$(echo "3 ^ 20" | bc)
  • i = RANDOM % 3: не POSIX. Наиболее близким в инструментальной панели POSIX является i=$(awk 'BEGIN{srand(); print int(rand() * 3)}').
LinuxSecurityFreak
источник
2

спасибо за глубокие знания о разнице. Падение замены, которое работает для меня при использовании shellcheck.net, было таким, как показано ниже.

BASH

for i in {1..100}; do  
  ...  
done  

POSIX

i=0; while [ $i -le 100 ]; do  
  ...  
  i=$(( i + 1 ))  
done

некоторые люди отметили, что seq также является опцией, использующей seq 1 10. Создание цикла, однако это зависит от того, что у os есть seq.

Джимми М.Г. Лим
источник
обратите внимание, что я поместил i = 0 в той же строке, что и while. хотя удобочитаемость не очень хорошая, это один вкладыш. Это гарантирует, что переменная i не испорчена нигде, определенной в скрипте.
Джимми М.Г. Лим