Wie kann ich eine arithmetische Schleife in einem POSIX-Shell-Skript erstellen?

12

Ich weiß, wie man eine arithmetische forSchleife erstellt bash.

Wie kann man eine äquivalente Schleife in einem POSIX-Shell-Skript ausführen?

Da es verschiedene Möglichkeiten gibt, dasselbe Ziel zu erreichen, können Sie Ihre eigene Antwort hinzufügen und ein wenig erläutern, wie es funktioniert.

Ein Beispiel für eine solche bashSchleife folgt:

#!/bin/bash
for (( i=1; i != 10; i++ ))
do
    echo "$i"
done
LinuxSecurityFreak
quelle
@ StéphaneChazelas, weil es ein Hinweis auf die Geschichte war, der auf einem Missverständnis zu beruhen schien, da das OP nicht darauf hinwies, dass es sich um eine Bash-Sache handelte, sondern lediglich um Bash als Beispiel. Es schien nicht wirklich relevant zu sein.
Terdon

Antworten:

13

Ich habe nützliche Informationen im Shellcheck.net-Wiki gefunden , ich zitiere:

  1. Bash:

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

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

Beachten Sie jedoch, dass i++es sich nicht um POSIX handelt, sondern dass es übersetzt werden müsste, beispielsweise nach i += 1oder i = i + 1.


Das obige Skript in der Frage kann also POSIX-weise unter Verwendung der folgenden Regeln neu geschrieben werden:

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

Hier können Sie es jedoch besser lesbar machen mit:

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

Wie in initweisen wir einen konstanten Wert zu, sodass wir keinen arithmetischen Ausdruck auswerten müssen. Das i != 10in testkann leicht in einen [Ausdruck übersetzt werden, und nextwenn wir eine Shell-Variablenzuweisung im Gegensatz zu einer Variablenzuweisung innerhalb eines arithmetischen Ausdrucks verwenden, können wir :die Notwendigkeit des Zitierens beseitigen.


Neben i++-> i = i + 1gibt es weitere Übersetzungen von ksh / bash-spezifischen Konstrukten, die nicht POSIX sind, die Sie möglicherweise ausführen müssen:

  • i=1, j=2. Der ,arithmetische Operator ist nicht wirklich POSIX (und widerspricht dem Dezimaltrennzeichen in einigen Gebietsschemas mit ksh93). Sie könnten es durch einen anderen Operator wie +in ersetzen, : "$(((i=1) + (j=2)))"aber die Verwendung i=1 j=2wäre viel besser lesbar.
  • a[0]=1: Keine Arrays in POSIX-Shells
  • i = 2**20: Kein Energieoperator in der POSIX-Shell-Syntax. <<wird jedoch unterstützt, so dass man für Zweierpotenzen verwenden kann i = 1 << 20. Für andere Mächte kann man auf Folgendes zurückgreifen bc:i=$(echo "3 ^ 20" | bc)
  • i = RANDOM % 3: nicht POSIX. Der nächstgelegene in der POSIX-Toolchest ist i=$(awk 'BEGIN{srand(); print int(rand() * 3)}').
LinuxSecurityFreak
quelle
2

Vielen Dank für das oben genannte Hintergrundwissen über den Unterschied. Ein Rückgang des Ersatzes, der bei Verwendung von shellcheck.net für mich funktioniert, war wie folgt.

BASH

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

POSIX

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

Einige Leute bemerkten, dass seq auch eine Option ist, die seq 1 10 verwendet. Das Erstellen einer Schleife hängt jedoch davon ab, dass os seq hat.

Jimmy MG Lim
quelle
beachte, dass ich i = 0 in die gleiche Zeile wie while gesetzt habe. Obwohl die Lesbarkeit nicht besonders gut ist, handelt es sich um ein Einzeiler-Drop-In. Dies stellt sicher, dass die Variable i nicht durch eine andere im Skript definierte Stelle beeinträchtigt wird.
Jimmy MG Lim