Jak iterować w zakresie liczb zdefiniowanych przez zmienne w Bash?


1541

Jak iterować w zakresie liczb w Bash, gdy zakres jest podany przez zmienną?

Wiem, że mogę to zrobić (zwane „wyrażeniem sekwencji” w dokumentacji Bash ):

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

Co daje:

1
2
3
4
5

Jak jednak zastąpić jeden z punktów końcowych zakresu zmienną? To nie działa:

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

Które wydruki:

{1..5}


26
Cześć wszystkim, informacje i wskazówki, które tu przeczytałem, są naprawdę pomocne. Myślę, że najlepiej unikać używania seq. Powodem jest to, że niektóre skrypty muszą być przenośne i muszą działać na wielu różnych systemach uniksowych, w których niektóre polecenia mogą nie być obecne. Dla przykładu, seq nie jest domyślnie obecny w systemach FreeBSD.


9
Nie pamiętam, która wersja Bash dokładnie, ale to polecenie obsługuje również zera końcowe. Co czasami jest naprawdę pomocne. Polecenie for i in {01..10}; do echo $i; donedałoby liczby takie jak 01, 02, 03, ..., 10.
topr

1
Dla takich jak ja, którzy chcą tylko iterować w zakresie indeksów tablicy , podstawową metodą byłoby: myarray=('a' 'b' 'c'); for i in ${!myarray[@]}; do echo $i; done(zwróć uwagę na wykrzyknik). To jest bardziej szczegółowe niż pierwotne pytanie, ale może pomóc. Zobacz rozszerzenia parametrów bash
PlasmaBinturong

1
Rozwinięcie nawiasu klamrowego jest również używane do wyrażeń takich, {jpg,png,gif}które nie są tutaj bezpośrednio omówione, chociaż odpowiedź będzie identyczna. Zobacz rozwinięcie nawiasu klamrowego ze zmienną? [duplikat], który jest oznaczony jako duplikat tego.
tripleee

Odpowiedzi:


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

edycja: Wolę seqinne metody, bo tak naprawdę pamiętam;)


36
seq obejmuje wykonanie zewnętrznego polecenia, które zwykle spowalnia działanie. Może to nie mieć znaczenia, ale staje się ważne, jeśli piszesz skrypt do obsługi dużej ilości danych.
paxdiablo

37
W porządku jak na jedną linijkę. Rozwiązanie Pax też jest w porządku, ale gdyby wydajność była naprawdę problemem, nie użyłbym skryptu powłoki.
eschercycle

17
seq jest wywoływany tylko raz, aby wygenerować liczby. exec () nie powinno być znaczące, chyba że ta pętla znajduje się w innej ciasnej pętli.
Javier

29
Polecenie zewnętrzne nie jest tak naprawdę istotne: jeśli martwisz się o obciążenie związane z uruchamianiem poleceń zewnętrznych, w ogóle nie chcesz używać skryptów powłoki, ale ogólnie w Uniksie obciążenie to jest niskie. Istnieje jednak problem z użyciem pamięci, jeśli END jest wysoki.
Mark Baker,

18
Zauważ, że to seq $ENDby wystarczyło, ponieważ domyślnie zaczyna się od 1. Od man seq: „Jeśli PIERWSZY lub INCREMENT zostanie pominięty, domyślnie wynosi 1”.
fedorqui „SO przestań szkodzić”

469

seqMetoda jest najprostsza, ale Bash ma wbudowany oceny arytmetycznej.

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

for ((expr1;expr2;expr3));Konstrukt działa podobnie jak for (expr1;expr2;expr3)w C i podobnych języków, jak i innych ((expr))przypadkach Bash traktuje je jako arytmetyka.


67
W ten sposób unika się obciążenia pamięci dużej listy i zależności od seq. Użyj tego!
bobbogo

3
@MarinSagovac To czyni pracę i nie ma żadnych błędów składniowych. Czy jesteś pewien, że twoja skorupa to Bash?
gniourf_gniourf

3
@MarinSagovac Upewnij się, że wprowadziłeś #!/bin/bashpierwszą linię skryptu. wiki.ubuntu.com/...
Melebius

6
bardzo krótkie pytanie na ten temat: dlaczego ((i = 1; i <= END; i ++)) AND NOT ((i = 1; i <= $ END; i ++)); dlaczego nie $ przed END?
Baedsch,

5
@ Baedsch: z tego samego powodu i nie jest używany jako $ i. stany strony podręcznika użytkownika bash do oceny arytmetycznej: „W wyrażeniu do zmiennych powłoki można również odwoływać się według nazwy bez użycia składni interpretacji parametrów”.
user3188140,

192

dyskusja

Używanie seqjest w porządku, jak sugerował Jiaaro. Pax Diablo zasugerował pętlę Bash, aby uniknąć wywołania podprocesu, z dodatkową zaletą polegającą na tym, że jest bardziej przyjazna dla pamięci, jeśli $ END jest zbyt duże. Zathrus zauważył typowy błąd w implementacji pętli, a także zasugerował, że skoro ijest zmienną tekstową, ciągłe konwersje na liczby tam i z powrotem są wykonywane z powiązanym spowolnieniem.

arytmetyka liczb całkowitych

To jest ulepszona wersja pętli Bash:

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

Jeśli jedyne, czego chcemy echo, to moglibyśmy pisać echo $((i++)).

ephemient nauczył mnie czegoś: Bash pozwala na for ((expr;expr;expr))konstrukcje. Ponieważ nigdy nie czytałem całej strony podręcznika użytkownika dla Basha (tak jak zrobiłem to ze kshstroną podręcznika powłoki Korn ( ) i to było dawno temu), przegapiłem to.

Więc,

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

wydaje się być najbardziej wydajnym sposobem na pamięć (nie będzie konieczne przydzielanie pamięci na zużycie seqdanych wyjściowych, co może być problemem, jeśli END jest bardzo duży), chociaż prawdopodobnie nie jest to „najszybszy”.

pierwsze pytanie

eschercycle zauważył, że notacja Bash { a .. b } działa tylko z literałami; prawda, zgodnie z instrukcją Bash. Można pokonać tę przeszkodę jednym (wewnętrznym) fork()bez exec()(jak w przypadku wywoływania seq, który jest innym obrazem wymaga fork + exec):

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

Zarówno evali echosą atakujących builtins, ale fork()są wymagane do zastąpienia polecenia (The $(…)konstrukcie).


1
Jedyną wadą pętli w stylu C jest to, że nie może ona używać argumentów wiersza poleceń, ponieważ zaczynają się od „$”.
karatedog

3
@karatedog: for ((i=$1;i<=$2;++i)); do echo $i; donew skrypcie działa dobrze na bash v.4.1.9, więc nie widzę problemu z argumentami wiersza poleceń. Masz na myśli coś innego?
tzot

Wydaje się, że rozwiązanie eval jest szybsze niż wbudowane w C-podobne dla: $ time for ((i = 1; i <= 100000; ++ i)); zrobić :; wykonano prawdziwy 0m21.220s użytkownik 0m19.763s sys 0m1.203s $ czas dla i w $ (echo ewaluacji „{1..100000}”); zrobić :; gotowy; real 0m13.881s użytkownik 0m13.536s sys 0m0.152s
Marcin Zaluski

3
Tak, ale eval jest zły ... @MarcinZaluski time for i in $(seq 100000); do :; donejest znacznie szybszy!
F. Hauri

Wydajność musi być zależna od platformy, ponieważ wersja eval jest najszybsza na moim komputerze.
Andrew Prock

102

Oto dlaczego oryginalne wyrażenie nie działało.

From man bash :

Rozwijanie nawiasów wykonywane jest przed innymi rozszerzeniami, a wszystkie znaki specjalne dla innych rozszerzeń są zachowywane w wyniku. Jest to ściśle tekstowe. Bash nie stosuje żadnej interpretacji składniowej w kontekście rozwinięcia lub tekstu między nawiasami klamrowymi.

Tak więc interpretacja nawiasu klamrowego jest wykonywana wcześnie jako czysto tekstowa operacja makra, przed rozszerzeniem parametru.

Powłoki to wysoce zoptymalizowane hybrydy między makroprocesorami a bardziej formalnymi językami programowania. Aby zoptymalizować typowe przypadki użycia, język jest bardziej złożony i pewne ograniczenia są akceptowane.

Rekomendacje

Sugeruję pozostanie przy funkcjach Posix 1 . Oznacza to użycie for i in <list>; do, jeśli lista jest już znana, w przeciwnym razie użyj whilelub seq, jak w:

#!/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 jest świetną powłoką i używam jej interaktywnie, ale nie umieszczam bash-isms w moich skryptach. Skrypty mogą wymagać szybszej powłoki, bezpieczniejszej, bardziej osadzonej. Może być konieczne uruchomienie na czymkolwiek zainstalowanym jako / bin / sh, a następnie są wszystkie zwykłe argumenty pro-standardów. Pamiętasz shellshock, alias bashdoor?


13
Nie mam mocy, ale przesunęłbym to trochę na górę listy, przede wszystkim bash-patrzenie w pępek, ale natychmiast po stylu C dla oceny pętli i arytmetyki.
wspólnik

2
Implikacją jest to, że rozwijanie nawiasów nie oszczędza dużo pamięci w porównaniu do seqdużych zakresów. Np. echo {1..1000000} | wcUjawnia, że ​​echo produkuje 1 linię, milion słów i 6 888 896 bajtów. Próba seq 1 1000000 | wcdaje milion wierszy, milion słów i 6 888 896 bajtów, a także jest ponad siedem razy szybsza, zgodnie z timekomendą.
George,

Uwaga: wcześniej wspomniałem o whilemetodzie POSIX : stackoverflow.com/a/31365662/895245 Ale cieszę się, że się zgadzasz :-)
Ciro Santilli 冠状 病毒 审查 六四 事件 法轮功

Uwzględniłem tę odpowiedź w poniższej odpowiedzi porównawczej. stackoverflow.com/a/54770805/117471 (To jest uwaga dla mnie, aby śledzić, które z nich zostawiłem.)
Bruno Bronosky,

@ mator Myślałem, że styl C dla pętli i obliczeń arytmetycznych są tym samym rozwiązaniem. Czy coś brakuje?
Oscar Zhang

71

Sposób POSIX

Jeśli zależy Ci na przenośności, skorzystaj z przykładu standardu POSIX :

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

Wynik:

2
3
4
5

Rzeczy, które nie są POSIX:


Właśnie otrzymałem 4 głosy poparcia dla tej odpowiedzi, co jest wysoce niezwykłe. Jeśli zostało to opublikowane na stronie z agregacją linków, proszę dać mi link, pozdrawiam.
Ciro Santilli 冠状 病毒 审查 六四 事件 法轮功

Cytat odnosi się xnie do całego wyrażenia. $((x + 1))jest w porządku.
chepner

Chociaż nie jest przenośny i różni się od GNU seq(BSD seqpozwala ustawić ciąg zakończenia sekwencji za pomocą -t), FreeBSD i NetBSD mają również odpowiednio seqod 9.0 i 3.0.
Adrian Günter

@CiroSantilli @chepner $((x+1))i $((x + 1))parse dokładnie tak samo, jak wtedy, gdy parser tokenizes x+1zostanie on podzielony na 3 tokeny: x, +, i 1. xnie jest poprawnym tokenem numerycznym, ale jest prawidłowym tokenem nazwy zmiennej, ale x+nie jest, dlatego podział. +jest poprawnym tokenem operatora arytmetycznego, ale +1nie jest, więc token jest tam ponownie dzielony. I tak dalej.
Adrian Günter,

Uwzględniłem tę odpowiedź w poniższej odpowiedzi porównawczej. stackoverflow.com/a/54770805/117471 (To jest uwaga dla mnie, aby śledzić, które z nich zostawiłem.)
Bruno Bronosky,

35

Kolejna warstwa pośrednictwa:

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

2
+1: Również eval 'for i w {1 ..' $ END '}; do ... eval wydaje się naturalnym sposobem rozwiązania tego problemu.
William Pursell

28

Możesz użyć

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

seq obejmuje wykonanie zewnętrznego polecenia, które zwykle spowalnia działanie.
paxdiablo

9
Nie wymaga wykonania zewnętrznego polecenia dla każdej iteracji, tylko raz. Jeśli problemem jest czas uruchomienia jednego polecenia zewnętrznego, używasz niewłaściwego języka.
Mark Baker,

1
Czy zagnieżdżanie jest jedynym przypadkiem, w którym ma to znaczenie? Zastanawiałem się, czy istnieje różnica w wydajności, czy jakiś nieznany techniczny efekt uboczny?
Sqeaky

@ Squeaky To osobne pytanie, na które odpowiedziano tutaj: stackoverflow.com/questions/4708549/…
tripleee

Uwzględniłem tę odpowiedź w poniższej odpowiedzi porównawczej. stackoverflow.com/a/54770805/117471 (To jest uwaga dla mnie, aby śledzić, które z nich zostawiłem.)
Bruno Bronosky,

21

Jeśli potrzebujesz prefiksu, może ci się to spodobać

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

to da

07
08
09
10
11
12

4
Nie printf "%02d\n" $ibyłoby łatwiej niż printf "%2.0d\n" $i |sed "s/ /0/"?
zb226

19

Jeśli korzystasz z BSD / OS X, możesz użyć jot zamiast seq:

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

17

Działa to dobrze w bash:

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

6
echo $((i++))działa i łączy go w jedną linię.
Bruno Bronosky


1
@Ciro, ponieważ pytanie wyraźnie określa bash i ma tag bash, myślę, że prawdopodobnie okaże się, że „rozszerzenia” bash są w porządku :-)
paxdiablo

@paxdiablo Nie mam na myśli, że to nie jest poprawne, ale dlaczego nie być przenośnym, kiedy możemy ;-)
Ciro Santilli 冠状 病毒 审查 六四 事件 法轮功

W bash, możemy po prostu zrobić while [[ i++ -le "$END" ]]; do(przyrostowy) przyrost w teście
Aaron McDaid

14

Połączyłem tutaj kilka pomysłów i zmierzyłem wydajność.

TL; DR Na wynos:

  1. seqi {..}są naprawdę szybkie
  2. fora whilepętle są wolne
  3. $( ) jest wolny
  4. for (( ; ; )) pętle są wolniejsze
  5. $(( )) jest jeszcze wolniejszy
  6. Martwienie się o N liczb w pamięci (sekw. Lub {..}) jest głupie (przynajmniej do 1 miliona).

To nie są wnioski . Będziesz musiał spojrzeć na kod C za każdym z nich, aby wyciągnąć wnioski. To więcej o tym, jak zwykle używamy każdego z tych mechanizmów do zapętlania kodu. Większość pojedynczych operacji jest wystarczająco zbliżona do tej samej prędkości, że w większości przypadków nie będzie to miało znaczenia. Ale takim mechanizmem for (( i=1; i<=1000000; i++ ))jest wiele operacji, które można zobaczyć wizualnie. Jest to również o wiele więcej operacji na pętlę niż z niej otrzymujesz for i in $(seq 1 1000000). I może to nie być dla ciebie oczywiste, dlatego wykonywanie takich testów jest tak cenne.

Dema

# 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

1
Miły! Nie zgadzaj się jednak ze swoim podsumowaniem. Wydaje mi się, że $(seq)jest o tej samej prędkości co {a..b}. Ponadto każda operacja trwa mniej więcej w tym samym czasie, więc dodaje mi około 4 μs do każdej iteracji pętli. Tutaj operacja to echo w ciele, porównanie arytmetyczne, przyrost itd. Czy któreś z tych rzeczy jest zaskakujące? Kogo to obchodzi, ile czasu zajmuje wykonanie niezbędnych czynności w pętli - środowisko wykonawcze prawdopodobnie będzie zdominowane przez zawartość pętli.
bobbogo,

@bobbogo masz rację, tak naprawdę chodzi o liczbę operacji. Zaktualizowałem swoją odpowiedź, aby to odzwierciedlić. Wiele wykonywanych przez nas połączeń faktycznie wykonuje więcej operacji, niż możemy się spodziewać. Zawęziłem to do listy około 50 testów, które przeprowadziłem. Spodziewałem się, że moje badania były zbyt nerdy nawet dla tego tłumu. Jak zawsze, sugeruję nadanie priorytetu twoim wysiłkom związanym z kodowaniem w taki sposób: Skróć go; Niech będzie czytelny; Zrób to szybciej; Zrób to przenośne. Często # 1 powoduje # 3. Nie marnuj czasu na # 4, dopóki nie musisz.
Bruno Bronosky

8

Wiem, że chodzi o to pytanie bash, ale - dla przypomnienia - ksh93jest mądrzejsze i implementuje je zgodnie z oczekiwaniami:

$ 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}

8

To jest inny sposób:

end=5
for i in $(bash -c "echo {1..${end}}"); do echo $i; done

1
Powoduje to odrodzenie kolejnej powłoki.
codeforester

1
W rzeczywistości jest to wyjątkowo okropne, ponieważ spawnuje 2 pociski, gdy wystarczy 1.
Bruno Bronosky

8

Jeśli chcesz pozostać jak najbliżej składni wyrażenia nawiasów klamrowych, wypróbuj rangefunkcję z bash-tricks 'range.bash .

Na przykład wszystkie poniższe czynności będą wykonywać dokładnie to samo, co echo {1..10}:

source range.bash
one=1
ten=10

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

Próbuje obsługiwać natywną składnię bash z możliwie najmniejszą liczbą „gotchas”: nie tylko obsługiwane są zmienne, ale również for i in {1..a}; do echo $i; donezapobiega się często niepożądanemu zachowaniu nieprawidłowych zakresów dostarczanych jako ciągi znaków (np. ).

Inne odpowiedzi będą działać w większości przypadków, ale wszystkie mają co najmniej jedną z następujących wad:

  • Wiele z nich używa podpowłok , które mogą mieć negatywny wpływ na wydajność i mogą nie być możliwe w niektórych systemach.
  • Wiele z nich polega na programach zewnętrznych. Nawet seqbinarny, który musi być zainstalowany, aby go użyć, musi zostać załadowany przez bash i musi zawierać oczekiwany program, aby działał w tym przypadku. Wszechobecny czy nie, na czym można polegać bardziej niż sam język Bash.
  • Rozwiązania korzystające tylko z natywnej funkcjonalności Bash, takie jak @ ephemient, nie będą działać na zakresach alfabetycznych, takich jak {a..z}; rozwinie nawias klamrowy. Pytanie dotyczyło jednak zakresów liczb , więc jest to spór.
  • Większość z nich nie jest wizualnie podobna do {1..10}składni zakresu rozszerzonego nawiasami, więc programy używające obu mogą być nieco trudniejsze do odczytania.
  • Odpowiedź @ bobbogo wykorzystuje pewną znaną składnię, ale robi coś nieoczekiwanego, jeśli $ENDzmienna nie jest poprawnym „bookend” zakresu dla drugiej strony zakresu. Jeśli END=ana przykład błąd nie wystąpi, a pełna wartość {1..a}zostanie wyświetlona. Jest to również domyślne zachowanie Bash - często jest to nieoczekiwane.

Oświadczenie: Jestem autorem powiązanego kodu.



6

Wszystkie są ładne, ale sekwencja jest podobno przestarzała i większość działa tylko z zakresami liczbowymi.

Jeśli umieścisz pętlę for w podwójnych cudzysłowach, zmienne początkowa i końcowa zostaną usunięte podczas echa łańcucha i możesz wysłać łańcuch z powrotem do BASH w celu wykonania. $imusi być poprzedzony znakiem \, więc NIE jest analizowany przed wysłaniem do podpowłoki.

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

Dane wyjściowe można również przypisać do zmiennej:

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

Jedynym „narzutem”, jaki powinno to wygenerować, powinno być drugie wystąpienie bash, więc powinno być odpowiednie do intensywnych operacji.


5

Jeśli wykonujesz polecenia powłoki i masz (podobnie jak ja) fetysz do potoków, ten jest dobry:

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


3

Można to zrobić na wiele sposobów, jednak te, które preferuję, podano poniżej

Za pomocą seq

Streszczenie z man seq

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

Składnia

Pełna komenda
seq first incr last

  • pierwszy to numer początkowy w sekwencji [jest opcjonalny, domyślnie: 1]
  • incr jest przyrostem [jest opcjonalne, domyślnie: 1]
  • ostatnia to ostatnia liczba w sekwencji

Przykład:

$ seq 1 2 10
1 3 5 7 9

Tylko z pierwszym i ostatnim:

$ seq 1 5
1 2 3 4 5

Tylko z ostatnim:

$ seq 5
1 2 3 4 5

Za pomocą {first..last..incr}

Tutaj pierwsze i ostatnie są obowiązkowe, a incr jest opcjonalne

Używanie tylko pierwszego i ostatniego

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

Korzystanie z incr

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

Możesz użyć tego nawet dla postaci takich jak poniżej

$ 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

3

jeśli nie chcesz używać „ seq” lub „ evaljotlub arytmetycznego formatu rozszerzenia np. for ((i=1;i<=END;i++))lub inne pętle np. while, a ty nie chcesz tylko printfi z przyjemnością echo, to proste obejście może pasować do Twojego budżetu:

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

PS: Mój bash i tak nie ma seqpolecenia „ ”.

Testowane na Mac OSX 10.6.8, Bash 3.2.48


0

Działa to w Bash i Korn, może również przechodzić z wyższych na niższe liczby. Prawdopodobnie nie jest najszybszy ani najładniejszy, ale działa wystarczająco dobrze. Obsługuje również negatywy.

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"
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.