2117 Stimmen

Wie kann ich in der Bash über einen durch Variablen definierten Zahlenbereich iterieren?

Wie kann ich in Bash über einen Zahlenbereich iterieren, wenn der Bereich durch eine Variable vorgegeben ist?

Ich weiß, dass ich dies tun kann (in der Bash "Sequenzausdruck" genannt) Dokumentation ):

 for i in {1..5}; do echo $i; done

Das ergibt:

1
2
3
4
5

Doch wie kann ich einen der Endpunkte des Bereichs durch eine Variable ersetzen? Das funktioniert nicht:

END=5
for i in {1..$END}; do echo $i; done

Welche Drucke:

{1..5}

41 Stimmen

Hallo zusammen, die Informationen und Tipps, die ich hier gelesen habe, sind alle sehr hilfreich. Ich denke, es ist am besten, die Verwendung von seq zu vermeiden. Der Grund dafür ist, dass einige Skripte portabel sein müssen und auf einer Vielzahl von Unix-Systemen laufen müssen, auf denen einige Befehle möglicherweise nicht vorhanden sind. Nur um ein Beispiel zu nennen: seq ist auf FreeBSD-Systemen standardmäßig nicht vorhanden.

12 Stimmen

Ich weiß nicht mehr, seit welcher Version der Bash genau, aber dieser Befehl unterstützt auch abschließende Nullen. Was manchmal wirklich hilfreich ist. Befehl for i in {01..10}; do echo $i; done würde Zahlen ergeben wie 01, 02, 03, ..., 10 .

2346voto

Jiaaro Punkte 70598
for i in $(seq 1 $END); do echo $i; done

edit: Ich bevorzuge seq gegenüber den anderen Methoden, weil ich sie mir tatsächlich merken kann ;)

720voto

ephemient Punkte 189038

En seq Methode ist die einfachste, aber die Bash hat eine eingebaute arithmetische Auswertung.

END=5
for ((i=1;i<=END;i++)); do
    echo $i
done
# ==> outputs 1 2 3 4 5 on separate lines

En for ((expr1;expr2;expr3)); Konstrukt funktioniert genau wie for (expr1;expr2;expr3) in C und ähnlichen Sprachen, und wie andere ((expr)) Fällen behandelt Bash sie wie Arithmetik.

218voto

tzot Punkte 86792

Diskussion

使用する seq ist in Ordnung, wie Jiaaro vorgeschlagen hat. Pax Diablo schlug eine Bash-Schleife vor, um den Aufruf eines Unterprozesses zu vermeiden, mit dem zusätzlichen Vorteil, dass sie speicherfreundlicher ist, wenn $END zu groß ist. Zathrus entdeckte einen typischen Fehler in der Schleifenimplementierung und wies auch darauf hin, dass, da i eine Textvariable ist, werden kontinuierliche Konvertierungen von und zu Zahlen mit einer entsprechenden Verlangsamung durchgeführt.

Ganzzahlarithmetik

Dies ist eine verbesserte Version der Bash-Schleife:

typeset -i i END
let END=5 i=1
while ((i<=END)); do
    echo $i
    …
    let i++
done

Wenn das einzige, was wir wollen, die echo dann könnten wir schreiben echo $((i++)) .

Ephemeride hat mich etwas gelehrt: Bash erlaubt for ((expr;expr;expr)) Konstrukte. Da ich noch nie die gesamte Manualseite der Bash gelesen habe (wie ich es bei der Korn-Shell getan habe ( ksh ) man page, und das ist schon lange her), habe ich das übersehen.

Also,

typeset -i i END # Let's be explicit
for ((i=1;i<=END;++i)); do echo $i; done

scheint der speichereffizienteste Weg zu sein (es wird nicht nötig sein, Speicher zuzuweisen, um die seq Ausgabe, was ein Problem sein könnte, wenn END sehr groß ist), wenn auch wahrscheinlich nicht die "schnellste".

die Ausgangsfrage

eschercycle stellte fest, dass die { a .. b } Die Bash-Notation funktioniert nur mit Literalen; true, entsprechend dem Bash-Handbuch. Man kann dieses Hindernis mit einem einzigen (internen) fork() ohne einen exec() (wie bei dem Aufruf von seq (ein weiteres Image, das einen Fork+exec erfordert):

for i in $(eval echo "{1..$END}"); do

Beide eval y echo sind Bash-Builtins, aber ein fork() ist für die Befehlssubstitution erforderlich (die $(…) Konstrukt).

118voto

DigitalRoss Punkte 138823

Hier ist der Grund, warum der ursprüngliche Ausdruck nicht funktioniert hat.

En Mannschaftsaufstellung :

Die Ausdehnung der Spange erfolgt vor vor allen anderen Expansionen durchgeführt, und alle Sonderzeichen für andere Expansionen werden im Ergebnis erhalten. Sie ist streng textbasiert. Bash wendet keine syntaktische Interpretation auf den Kontext der der Expansion oder des Textes zwischen den geschweiften Klammern.

Also, Spreizung ist etwas, das frühzeitig als rein textuelle Makrooperation durchgeführt wird, bevor Parametererweiterung.

Shells sind hochoptimierte Hybride zwischen Makroprozessoren und formaleren Programmiersprachen. Um die typischen Anwendungsfälle zu optimieren, wird die Sprache etwas komplexer gestaltet und es werden einige Einschränkungen in Kauf genommen.

Empfehlung

Ich würde vorschlagen, bei Posix zu bleiben 1 Merkmale. Dies bedeutet die Verwendung for i in <list>; do , wenn die Liste bereits bekannt ist, andernfalls verwenden Sie while o seq , wie in:

#!/bin/sh

limit=4

i=1; while [ $i -le $limit ]; do
  echo $i
  i=$(($i + 1))
done
# Or -----------------------
for i in $(seq 1 $limit); do
  echo $i
done

1. Bash ist eine großartige Shell und ich benutze sie interaktiv, aber ich füge keine Bash-ismen in meine Skripte ein. Skripte brauchen vielleicht eine schnellere Shell, eine sicherere, eine besser eingebettete. Sie müssen vielleicht auf einer Shell laufen, die als /bin/sh installiert ist, und dann gibt es noch die üblichen Argumente für Standards. Erinnern Sie sich an . Shellshock, alias bashdoor?

99voto

Der POSIX-Weg

Wenn Sie Wert auf Übertragbarkeit legen, verwenden Sie die Beispiel aus dem POSIX-Standard :

i=2
end=5
while [ $i -le $end ]; do
    echo $i
    i=$(($i+1))
done

Sortie :

2
3
4
5

Dinge, die sind pas POSIX:

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X