Wie iteriere ich über einen Bereich von Zahlen, die durch Variablen in Bash definiert sind?

1543

Wie iteriere ich in Bash über einen Zahlenbereich, wenn der Bereich durch eine Variable angegeben wird?

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

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

Welches gibt:

1
2
3
4
5

Wie kann ich jedoch einen der Bereichsendpunkte durch eine Variable ersetzen? Das funktioniert nicht:

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

Welche Drucke:

{1..5}

Eschercycle
quelle
26
Hallo zusammen, die Informationen und Hinweise, 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 und auf einer Vielzahl von Unix-Systemen ausgeführt werden 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.
9
Ich weiß nicht mehr genau, seit welcher Version von Bash, aber dieser Befehl unterstützt auch nachgestellte Nullen. Was manchmal sehr hilfreich ist. Befehl for i in {01..10}; do echo $i; donewürde Zahlen wie geben 01, 02, 03, ..., 10.
15.
1
Für diejenigen wie mich, die nur über den Indexbereich eines Arrays iterieren möchten, wäre der Bash-Weg: myarray=('a' 'b' 'c'); for i in ${!myarray[@]}; do echo $i; done(Beachten Sie das Ausrufezeichen). Es ist spezifischer als die ursprüngliche Frage, könnte aber helfen. Siehe Bash-Parametererweiterungen
PlasmaBinturong
1
Die Klammererweiterung wird auch für Ausdrücke verwendet, {jpg,png,gif}die hier nicht direkt angesprochen werden, obwohl die Antwort identisch ist. Siehe Klammererweiterung mit Variable? [Duplikat], das als Duplikat von diesem markiert ist.
Tripleee

Antworten:

1746
for i in $(seq 1 $END); do echo $i; done

edit: Ich bevorzuge seqdie anderen Methoden, weil ich mich tatsächlich daran erinnern kann;)

Jiaaro
quelle
36
seq beinhaltet die Ausführung eines externen Befehls, der normalerweise die Dinge verlangsamt. Dies spielt möglicherweise keine Rolle, ist jedoch wichtig, wenn Sie ein Skript für die Verarbeitung vieler Daten schreiben.
Paxdiablo
37
Gut für einen Einzeiler. Die Lösung von Pax ist auch in Ordnung, aber wenn die Leistung wirklich ein Problem wäre, würde ich kein Shell-Skript verwenden.
Eschercycle
17
seq wird nur einmal aufgerufen, um die Zahlen zu generieren. Die Ausführung () sollte nicht signifikant sein, es sei denn, diese Schleife befindet sich in einer anderen engen Schleife.
Javier
29
Der externe Befehl ist nicht wirklich relevant: Wenn Sie sich Sorgen über den Overhead beim Ausführen externer Befehle machen, möchten Sie überhaupt keine Shell-Skripte verwenden, aber im Allgemeinen ist der Overhead unter Unix gering. Es gibt jedoch das Problem der Speichernutzung, wenn END hoch ist.
Mark Baker
18
Beachten Sie, dass seq $ENDdies ausreichen würde, da der Standardwert bei 1 beginnt. Von man seq: "Wenn FIRST oder INCREMENT weggelassen wird, wird standardmäßig 1 verwendet."
Fedorqui 'SO hör auf zu schaden'
474

Die seqMethode ist die einfachste, aber Bash verfügt über eine integrierte arithmetische Auswertung.

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

Das for ((expr1;expr2;expr3));Konstrukt funktioniert genau wie for (expr1;expr2;expr3)in C und ähnlichen Sprachen, und wie in anderen ((expr))Fällen behandelt Bash sie als arithmetisch.

kurzlebig
quelle
67
Auf diese Weise wird der Speicheraufwand einer großen Liste und eine Abhängigkeit von vermieden seq. Benutze es!
Bobbogo
3
@MarinSagovac Dies funktioniert und es gibt keine Syntaxfehler. Bist du sicher, dass deine Shell Bash ist?
gniourf_gniourf
3
@MarinSagovac Stellen Sie sicher, dass Sie #!/bin/bashdie erste Zeile Ihres Skripts erstellen . wiki.ubuntu.com/…
Melebius
7
nur eine sehr kurze Frage dazu: warum ((i = 1; i <= END; i ++)) UND NICHT ((i = 1; i <= $ END; i ++)); warum kein $ vor ENDE?
Baedsch
5
@ Baedsch: aus dem gleichen Grund wird ich nicht als $ i verwendet. In der Bash-Manpage heißt es für die arithmetische Auswertung: "Innerhalb eines Ausdrucks können Shell-Variablen auch ohne Verwendung der Parametererweiterungssyntax namentlich referenziert werden."
user3188140
193

Diskussion

Die Verwendung seqist in Ordnung, wie Jiaaro vorgeschlagen hat. Pax Diablo schlug eine Bash-Schleife vor, um das Aufrufen eines Unterprozesses zu vermeiden, mit dem zusätzlichen Vorteil, speicherfreundlicher zu sein, wenn $ END zu groß ist. Zathrus entdeckte einen typischen Fehler in der Schleifenimplementierung und deutete auch an, dass ifortlaufende Konvertierungen hin und her mit einer damit verbundenen Verlangsamung durchgeführt werden , da es sich um eine Textvariable handelt.

Ganzzahlige Arithmetik

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, das ist echo, dann könnten wir schreiben echo $((i++)).

Ephemient hat mir etwas beigebracht: Bash erlaubt for ((expr;expr;expr))Konstrukte. Da ich nie die gesamte Manpage für Bash gelesen habe (wie ich es mit der kshManpage von Korn shell ( ) getan habe , und das ist lange her), habe ich das verpasst.

Damit,

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 ist nicht erforderlich, Speicher für die seqAusgabe des Verbrauchs zuzuweisen , was ein Problem sein könnte, wenn END sehr groß ist), obwohl dies wahrscheinlich nicht der „schnellste“ ist.

die erste Frage

eschercycle stellte fest, dass die Bash-Notation { a .. b } nur mit Literalen funktioniert. wahr, entsprechend dem Bash-Handbuch. Man kann dieses Hindernis mit einem einzigen (internen) fork()ohne ein überwinden exec()(wie es beim Aufrufen der Fall ist seq, da für ein anderes Bild eine Gabel + Exec erforderlich ist):

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

Beide evalund echosind Bash-Builtins, aber fork()für die Befehlssubstitution (das $(…)Konstrukt) ist a erforderlich .

tzot
quelle
1
Der einzige Nachteil der C-Style-Schleife ist, dass sie keine Befehlszeilenargumente verwenden kann, da sie mit "$" beginnen.
Karatedog
3
@karatedog: for ((i=$1;i<=$2;++i)); do echo $i; doneIn einem Skript funktioniert es gut mit Bash v.4.1.9, daher sehe ich kein Problem mit Befehlszeilenargumenten. Meinst du etwas anderes?
Zot
Es scheint, dass die Evaluierungslösung schneller ist als die C-ähnliche Lösung für: $ time for ((i = 1; i <= 100000; ++ i)); tun :; real gemacht 0m21.220s Benutzer 0m19.763s sys 0m1.203s $ Zeit für i in $ (eval echo "{1..100000}"); tun :; erledigt; real 0m13.881s Benutzer 0m13.536s sys 0m0.152s
Marcin Zaluski
3
Ja, aber eval ist böse ... @MarcinZaluski time for i in $(seq 100000); do :; doneist viel schneller!
F. Hauri
Die Leistung muss plattformspezifisch sein, da die Evaluierungsversion auf meinem Computer am schnellsten ist.
Andrew Prock
103

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

Von Mann schlagen :

Die Klammererweiterung wird vor allen anderen Erweiterungen durchgeführt, und alle für andere Erweiterungen speziellen Zeichen bleiben im Ergebnis erhalten. Es ist streng textuell. Bash wendet keine syntaktische Interpretation auf den Kontext der Erweiterung oder den Text zwischen den geschweiften Klammern an.

Die Klammererweiterung wird also früh als rein textuelle Makrooperation vor der Parametererweiterung durchgeführt.

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

Empfehlung

Ich würde vorschlagen, bei den Funktionen von Posix 1 zu bleiben . Dies bedeutet, dass Sie for i in <list>; do, wenn die Liste bereits bekannt ist, andernfalls verwenden whileoder seqwie 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 benötigen möglicherweise eine schnellere, sicherere und eingebettete Shell. Sie müssen möglicherweise auf dem als / bin / sh installierten Computer ausgeführt werden, und dann gibt es alle üblichen Pro-Standard-Argumente. Erinnerst du dich an Shellshock, auch bekannt als Bashdoor?

DigitalRoss
quelle
13
Ich habe nicht die Kraft, aber ich würde dies einiges in der Liste nach oben verschieben, vor allem das Bash-Nabel-Betrachten, aber unmittelbar nach dem C-Stil für die Schleifen- und arithmetische Auswertung.
Mateor
2
Eine Implikation ist, dass die Klammererweiterung im Vergleich zu seqgroßen Bereichen nicht viel Speicher spart . echo {1..1000000} | wcZeigt beispielsweise, dass das Echo 1 Zeile, eine Million Wörter und 6.888.896 Bytes erzeugt. Der Versuch seq 1 1000000 | wcergibt eine Million Zeilen, eine Million Wörter und 6.888.896 Bytes und ist auch mehr als siebenmal schneller, gemessen mit dem timeBefehl.
George
Hinweis: Ich hatte die POSIX- whileMethode bereits in meiner Antwort erwähnt: stackoverflow.com/a/31365662/895245 Aber ich bin froh, dass Sie zustimmen :-)
Ciro Santilli 4 病毒 审查 六四. 法轮功
Ich habe diese Antwort in meine Antwort zum Leistungsvergleich unten aufgenommen. stackoverflow.com/a/54770805/117471 (Dies ist eine Notiz für mich, um zu verfolgen, welche ich noch zu tun habe.)
Bruno Bronosky
@mateor Ich dachte, C-Stil für Schleife und arithmetische Auswertung sind die gleiche Lösung. Vermisse ich etwas
Oscar Zhang
73

Der POSIX-Weg

Wenn Sie sich für Portabilität interessieren, verwenden Sie das Beispiel aus dem POSIX-Standard :

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

Ausgabe:

2
3
4
5

Dinge, die nicht POSIX sind:

Ciro Santilli 冠状 病毒 审查 六四 事件 法轮功
quelle
Hatte gerade 4 positive Stimmen zu dieser Antwort, was sehr ungewöhnlich ist. Wenn dies auf einer Website zur Linkaggregation veröffentlicht wurde, geben Sie mir bitte einen Link, Prost.
Ciro Santilli 法轮功 病毒 审查. 事件 4
Das Zitat bezieht sich xnicht auf den gesamten Ausdruck. $((x + 1))ist in Ordnung.
Chepner
FreeBSD und NetBSD sind zwar nicht portabel und unterscheiden sich von GNU seq(mit BSD seqkönnen Sie eine Sequenzabschlusszeichenfolge festlegen -t). Sie haben jedoch seqseit 9.0 bzw. 3.0 auch.
Adrian Günter
@CiroSantilli @chepner $((x+1))und $((x + 1))Parse genau das gleiche, wie wenn der Parser tokenizes x+1wird es aufgeteilt in 3 - Token sein: x, +, und 1. xist kein gültiges numerisches Token, aber es ist ein gültiges Variablennamen-Token, x+ist es jedoch nicht, daher die Aufteilung. +ist ein gültiges arithmetisches Operator-Token, +1ist es aber nicht, daher wird das Token dort erneut aufgeteilt. Und so weiter.
Adrian Günter
Ich habe diese Antwort in meine Antwort zum Leistungsvergleich unten aufgenommen. stackoverflow.com/a/54770805/117471 (Dies ist eine Notiz für mich, um zu verfolgen, welche ich noch zu tun habe.)
Bruno Bronosky
35

Eine weitere Indirektionsebene:

for i in $(eval echo {1..$END}); do
    
Bobbogo
quelle
2
+1: Auch eval 'für i in {1 ..' $ END '}; do ... 'eval scheint der natürliche Weg zu sein, um dieses Problem zu lösen.
William Pursell
28

Sie können verwenden

for i in $(seq $END); do echo $i; done
Peter Hoffmann
quelle
seq beinhaltet die Ausführung eines externen Befehls, der normalerweise die Dinge verlangsamt.
Paxdiablo
9
Es wird nicht nur einmal ein externer Befehl für jede Iteration ausgeführt. Wenn die Zeit zum Starten eines externen Befehls ein Problem darstellt, verwenden Sie die falsche Sprache.
Mark Baker
1
Ist das Verschachteln also der einzige Fall, in dem dies wichtig ist? Ich habe mich gefragt, ob es einen Leistungsunterschied oder einen unbekannten technischen Nebeneffekt gibt.
Sqeaky
@ Queaky Das ist eine separate Frage, die hier beantwortet wird: stackoverflow.com/questions/4708549/…
Tripleee
Ich habe diese Antwort in meine Antwort zum Leistungsvergleich unten aufgenommen. stackoverflow.com/a/54770805/117471 (Dies ist eine Notiz für mich, um zu verfolgen, welche ich noch zu tun habe.)
Bruno Bronosky
21

Wenn Sie es Präfix benötigen, als Sie dies mögen könnten

 for ((i=7;i<=12;i++)); do echo `printf "%2.0d\n" $i |sed "s/ /0/"`;done

das wird nachgeben

07
08
09
10
11
12
Hossbear
quelle
4
Wäre nicht printf "%02d\n" $ieinfacher als printf "%2.0d\n" $i |sed "s/ /0/"?
zb226
19

Wenn Sie mit BSD / OS X arbeiten, können Sie jot anstelle von seq verwenden:

for i in $(jot $END); do echo $i; done
jefeveizen
quelle
17

Dies funktioniert gut in bash:

END=5
i=1 ; while [[ $i -le $END ]] ; do
    echo $i
    ((i = i + 1))
done
paxdiablo
quelle
6
echo $((i++))arbeitet und kombiniert es in einer Zeile.
Bruno Bronosky
1
Dies hat nicht benötigte Bash-Erweiterungen. Eine POSIX-Version: stackoverflow.com/a/31365662/895245
Ciro Santilli 法轮功 病毒 审查 六四. 法轮功
1
@Ciro, da die Frage speziell Bash angibt und ein Bash-Tag hat, werden Sie wahrscheinlich feststellen, dass Bash-Erweiterungen mehr als in Ordnung sind :-)
paxdiablo
@paxdiablo Ich meine nicht, dass es nicht korrekt ist, aber warum nicht tragbar sein, wenn wir können ;-)
Ciro Santilli 法轮功 病毒 审查 六四 事件 法轮功
In bashkönnen wir einfach while [[ i++ -le "$END" ]]; dodas (Post-) Inkrement im Test machen
Aaron McDaid
14

Ich habe einige der Ideen hier kombiniert und die Leistung gemessen.

TL; DR Takeaways:

  1. seqund {..}sind wirklich schnell
  2. forund whileSchleifen sind langsam
  3. $( ) ist langsam
  4. for (( ; ; )) Schleifen sind langsamer
  5. $(( )) ist noch langsamer
  6. Sich um N Zahlen im Speicher (seq oder {..}) zu sorgen, ist albern (mindestens bis zu 1 Million).

Dies sind keine Schlussfolgerungen . Sie müssten sich den C-Code hinter jedem dieser Codes ansehen, um Schlussfolgerungen zu ziehen. Hier geht es mehr darum, wie wir dazu neigen, jeden dieser Mechanismen zum Durchlaufen von Code zu verwenden. Die meisten Einzeloperationen sind nahe genug an der Geschwindigkeit, die in den meisten Fällen keine Rolle spielt. Aber ein Mechanismus wie for (( i=1; i<=1000000; i++ ))ist viele Operationen, wie Sie visuell sehen können. Es sind auch viel mehr Operationen pro Schleife, als Sie erhalten for i in $(seq 1 1000000). Und das ist für Sie vielleicht nicht offensichtlich, weshalb es wertvoll ist, solche Tests durchzuführen.

Demos

# show that seq is fast
$ time (seq 1 1000000 | wc)
 1000000 1000000 6888894

real    0m0.227s
user    0m0.239s
sys     0m0.008s

# show that {..} is fast
$ time (echo {1..1000000} | wc)
       1 1000000 6888896

real    0m1.778s
user    0m1.735s
sys     0m0.072s

# Show that for loops (even with a : noop) are slow
$ time (for i in {1..1000000} ; do :; done | wc)
       0       0       0

real    0m3.642s
user    0m3.582s
sys 0m0.057s

# show that echo is slow
$ time (for i in {1..1000000} ; do echo $i; done | wc)
 1000000 1000000 6888896

real    0m7.480s
user    0m6.803s
sys     0m2.580s

$ time (for i in $(seq 1 1000000) ; do echo $i; done | wc)
 1000000 1000000 6888894

real    0m7.029s
user    0m6.335s
sys     0m2.666s

# show that C-style for loops are slower
$ time (for (( i=1; i<=1000000; i++ )) ; do echo $i; done | wc)
 1000000 1000000 6888896

real    0m12.391s
user    0m11.069s
sys     0m3.437s

# show that arithmetic expansion is even slower
$ time (i=1; e=1000000; while [ $i -le $e ]; do echo $i; i=$(($i+1)); done | wc)
 1000000 1000000 6888896

real    0m19.696s
user    0m18.017s
sys     0m3.806s

$ time (i=1; e=1000000; while [ $i -le $e ]; do echo $i; ((i=i+1)); done | wc)
 1000000 1000000 6888896

real    0m18.629s
user    0m16.843s
sys     0m3.936s

$ time (i=1; e=1000000; while [ $i -le $e ]; do echo $((i++)); done | wc)
 1000000 1000000 6888896

real    0m17.012s
user    0m15.319s
sys     0m3.906s

# even a noop is slow
$ time (i=1; e=1000000; while [ $((i++)) -le $e ]; do :; done | wc)
       0       0       0

real    0m12.679s
user    0m11.658s
sys 0m1.004s
Bruno Bronosky
quelle
1
Nett! Stimmen Sie Ihrer Zusammenfassung jedoch nicht zu. Sieht für mich so aus, als wäre $(seq)es ungefähr so ​​schnell wie {a..b}. Außerdem dauert jede Operation ungefähr dieselbe Zeit, sodass für jede Iteration der Schleife ungefähr 4 μs hinzugefügt werden. Hier ist eine Operation ein Echo im Körper, ein arithmetischer Vergleich, ein Inkrement usw. Ist irgendetwas davon überraschend? Wen interessiert es, wie lange es dauert, bis die Schleifenutensilien ihre Arbeit erledigt haben - die Laufzeit wird wahrscheinlich vom Inhalt der Schleife dominiert.
Bobbogo
@ Bobbogo Sie haben Recht, es geht wirklich um die Anzahl der Operationen. Ich habe meine Antwort aktualisiert, um dies widerzuspiegeln. Viele Anrufe, die wir tätigen, führen tatsächlich mehr Operationen aus, als wir vielleicht erwarten. Ich habe dies von einer Liste von ungefähr 50 Tests eingegrenzt, die ich durchgeführt habe. Ich erwartete, dass meine Forschung selbst für diese Menge zu nerdig war. Wie immer schlage ich vor, Ihre Codierungsbemühungen wie folgt zu priorisieren: Machen Sie es kürzer; Machen Sie es lesbar; Mach es schneller; Machen Sie es tragbar. Oft verursacht # 1 # 3. Verschwenden Sie Ihre Zeit nicht mit # 4, bis Sie müssen.
Bruno Bronosky
8

Ich weiß, dass es um diese Frage geht bash, aber - nur zur Veranschaulichung - sie ksh93ist schlauer und setzt sie wie erwartet um:

$ ksh -c 'i=5; for x in {1..$i}; do echo "$x"; done'
1
2
3
4
5
$ ksh -c 'echo $KSH_VERSION'
Version JM 93u+ 2012-02-29

$ bash -c 'i=5; for x in {1..$i}; do echo "$x"; done'
{1..5}
Adrian Frühwirth
quelle
8

Dies ist ein anderer Weg:

end=5
for i in $(bash -c "echo {1..${end}}"); do echo $i; done
Jahid
quelle
1
Dies hat den Aufwand, eine weitere Shell zu erzeugen.
Codeforester
1
Eigentlich ist das besonders schrecklich, weil es 2 Muscheln hervorbringt, wenn 1 ausreichen würde.
Bruno Bronosky
8

Wenn Sie so nah wie möglich an der Syntax des Klammerausdrucks bleiben möchten, probieren Sie die rangeFunktion aus Bash-Tricks ausrange.bash .

Zum Beispiel machen alle folgenden Aktionen genau dasselbe wie echo {1..10}:

source range.bash
one=1
ten=10

range {$one..$ten}
range $one $ten
range {1..$ten}
range {1..10}

Es wird versucht, die native Bash-Syntax mit möglichst wenigen "Fallstricken" zu unterstützen: Es werden nicht nur Variablen unterstützt, sondern auch das häufig unerwünschte Verhalten ungültiger Bereiche, die als Zeichenfolgen (z. B. for i in {1..a}; do echo $i; done) angegeben werden, wird verhindert.

Die anderen Antworten funktionieren in den meisten Fällen, haben jedoch alle mindestens einen der folgenden Nachteile:

  • Viele von ihnen verwenden Subshells , die die Leistung beeinträchtigen können und auf einigen Systemen möglicherweise nicht möglich sind.
  • Viele von ihnen sind auf externe Programme angewiesen. Even seqist eine Binärdatei, die installiert sein muss, um verwendet zu werden, von bash geladen werden muss und das erwartete Programm enthalten muss, damit sie in diesem Fall funktioniert. Allgegenwärtig oder nicht, das ist viel mehr als nur die Bash-Sprache.
  • Lösungen, die nur native Bash-Funktionen verwenden, wie z. B. @ ephemient, funktionieren nicht in alphabetischen Bereichen, wie z {a..z}. Klammer Erweiterung wird. Die Frage betraf jedoch Zahlenbereiche , daher ist dies ein Streitpunkt.
  • Die meisten von ihnen ähneln optisch nicht der {1..10}Syntax für den erweiterten Bereich, daher sind Programme, die beide verwenden, möglicherweise etwas schwieriger zu lesen.
  • Die Antwort von @ bobbogo verwendet einige der bekannten Syntax, führt jedoch zu unerwarteten $ENDEreignissen, wenn die Variable kein gültiger Bereich "Buchstütze" für die andere Seite des Bereichs ist. Wenn END=azum Beispiel kein Fehler auftritt und der wörtliche Wert wiedergegeben {1..a}wird. Dies ist auch das Standardverhalten von Bash - es ist nur oft unerwartet.

Haftungsausschluss: Ich bin der Autor des verlinkten Codes.

Zac B.
quelle
7

Ersetzen {}durch (( )):

tmpstart=0;
tmpend=4;

for (( i=$tmpstart; i<=$tmpend; i++ )) ; do 
echo $i ;
done

Ausbeuten:

0
1
2
3
4
BashTheKeyboard
quelle
Ich habe diese Antwort in meine Antwort zum Leistungsvergleich unten aufgenommen. stackoverflow.com/a/54770805/117471 (Dies ist eine Notiz für mich, um zu verfolgen, welche ich noch zu tun habe.)
Bruno Bronosky
6

Diese sind alle nett, aber seq ist angeblich veraltet und die meisten arbeiten nur mit numerischen Bereichen.

Wenn Sie Ihre for-Schleife in doppelte Anführungszeichen setzen, werden die Start- und Endvariablen beim Echo der Zeichenfolge dereferenziert, und Sie können die Zeichenfolge zur Ausführung direkt an BASH zurücksenden. $imuss mit \ 's maskiert werden, damit es NICHT ausgewertet wird, bevor es an die Subshell gesendet wird.

RANGE_START=a
RANGE_END=z
echo -e "for i in {$RANGE_START..$RANGE_END}; do echo \\${i}; done" | bash

Dieser Ausgang kann auch einer Variablen zugeordnet werden:

VAR=`echo -e "for i in {$RANGE_START..$RANGE_END}; do echo \\${i}; done" | bash`

Der einzige "Overhead", den dies erzeugen sollte, sollte die zweite Instanz von Bash sein, damit sie für intensive Operationen geeignet ist.

SuperBob
quelle
5

Wenn Sie Shell-Befehle ausführen und (wie ich) einen Fetisch für Pipelining haben, ist dieser gut:

seq 1 $END | xargs -I {} echo {}

Alex Spangher
quelle
3

Es gibt viele Möglichkeiten, dies zu tun, aber die, die ich bevorzuge, sind unten angegeben

Verwenden von seq

Inhaltsangabe von man seq

$ seq [-w] [-f format] [-s string] [-t string] [first [incr]] last

Syntax

Voller Befehl
seq first incr last

  • Das erste ist die Startnummer in der Sequenz [ist standardmäßig optional: 1]
  • incr ist Inkrement [ist standardmäßig optional: 1]
  • last ist die letzte Nummer in der Sequenz

Beispiel:

$ seq 1 2 10
1 3 5 7 9

Nur mit erstem und letztem:

$ seq 1 5
1 2 3 4 5

Nur mit letzter:

$ seq 5
1 2 3 4 5

Verwenden von {first..last..incr}

Hier sind zuerst und zuletzt obligatorisch und inkr ist optional

Verwenden Sie nur zuerst und zuletzt

$ echo {1..5}
1 2 3 4 5

Mit Inkr

$ echo {1..10..2}
1 3 5 7 9

Sie können dies auch für Zeichen wie unten verwenden

$ echo {a..z}
a b c d e f g h i j k l m n o p q r s t u v w x y z
theBuzzyCoder
quelle
3

Wenn Sie nicht das Format ' seq' oder ' eval' oder jotoder das arithmetische Erweiterungsformat verwenden möchten, z. for ((i=1;i<=END;i++))oder andere Schleifen, z. while, und Sie möchten nicht nur printfglücklich sein echo, dann könnte diese einfache Problemumgehung zu Ihrem Budget passen:

a=1; b=5; d='for i in {'$a'..'$b'}; do echo -n "$i"; done;' echo "$d" | bash

PS: Meine Bash hat seqsowieso keinen ' ' Befehl.

Getestet unter Mac OSX 10.6.8, Bash 3.2.48

Zimba
quelle
0

Dies funktioniert in Bash und Korn, kann auch von höheren zu niedrigeren Zahlen gehen. Wahrscheinlich nicht am schnellsten oder am schönsten, funktioniert aber gut genug. Behandelt auch Negative.

function num_range {
   # Return a range of whole numbers from beginning value to ending value.
   # >>> num_range start end
   # start: Whole number to start with.
   # end: Whole number to end with.
   typeset s e v
   s=${1}
   e=${2}
   if (( ${e} >= ${s} )); then
      v=${s}
      while (( ${v} <= ${e} )); do
         echo ${v}
         ((v=v+1))
      done
   elif (( ${e} < ${s} )); then
      v=${s}
      while (( ${v} >= ${e} )); do
         echo ${v}
         ((v=v-1))
      done
   fi
}

function test_num_range {
   num_range 1 3 | egrep "1|2|3" | assert_lc 3
   num_range 1 3 | head -1 | assert_eq 1
   num_range -1 1 | head -1 | assert_eq "-1"
   num_range 3 1 | egrep "1|2|3" | assert_lc 3
   num_range 3 1 | head -1 | assert_eq 3
   num_range 1 -1 | tail -1 | assert_eq "-1"
}
Ethan Post
quelle