Jak mogę utworzyć skrypt, który będzie liczony według piątki?

10

Próbowałem stworzyć bardzo prosty skrypt bash, aby wyświetlić wszystkie wielokrotności pięciu między 375 a 3500 (375, 380, 385 ...). Jedną z rzeczy, które próbowałem i nie działałem, są:

for i in {375..3500}
do
        echo $i
        (($i += 5))
done

Po pewnym czasie poddałem się i napisałem to w języku BASIC w około 15 sekund:

10 count = 375
20 print count
30 count = count+5
40 if count < 3500 then goto 20

Jak mogę zrobić mój program BASIC w skrypcie bash?

popiół
źródło
lepiej w przypadku przepełnienia stosu - w systemach Unix i Linux chodzi o system operacyjny, a nie o kodowanie. Kodowanie dotyczy przepływu stosu.
noɥʇʎԀʎzɐɹƆ

Odpowiedzi:

20

Alternatywnie można zastosować tradycyjny styl pętli typu C:

for ((i=375; i<=3500; i+=5)); do
    echo $i
done

Jest to być może mniej jasne niż użycie seq, ale nie powoduje żadnych podprocesów. Chociaż odkąd znam C, nie miałbym żadnych trudności ze zrozumieniem tego, ale YMMV.

Muzer
źródło
1
Minusem tej metody jest to, że jest mniej przenośna. Metoda seq działa w POSIX sh.
Wouter Verhelst
Och, czy styl C dla pętli nie istnieje w POSIX sh? Każdego dnia uczysz się czegoś nowego ...
Muzer
2
@WouterVerhelst Cóż, z wyjątkiem sytuacji, gdy jesteś ograniczony do POSIX sh, prawdopodobnie nie będziesz mieć seq, który jest częścią jądra GNU. busybox ma bardziej ograniczoną implementację, ale poza tym wiele systemów wbudowanych prawdopodobnie go nie ma.
Chris Down,
1
@Muzer - przynajmniej dashgo nie obsługuje. @ChrisDown - Nie ma powodu, dla którego „POSIX sh” musi oznaczać „brak seq”. Ale tak, oczywiście, przeoczyłem fakt, że seq nie jest określony przez POSIX.
Wouter Verhelst
27

Ponieważ i tak używasz nawiasów klamrowych, więc w pełni skorzystaj z jego funkcji:

echo {375..3500..5}

Możesz również użyć tej techniki, aby wydrukować każdy numer w osobnej linii z opcjonalnym tekstem, używając printfzamiast echo, na przykład:

$ printf "Number %s is generated.\n" {375..3500..5}
Number 375 is generated.
Number 380 is generated.
Number 385 is generated.
...

Edytować

Jak zauważył @kojiro w komentarzu, Mac OS używa bash 3 jako domyślnej powłoki, która nie obsługuje przyrostu wyrażania sekwencji rozwinięcia nawiasu. Musisz zaktualizować do wersji bash 4 lub użyć innej powłoki, która to obsługuje (np. Najnowszy zsh).

jimmij
źródło
2
To nie działa dla mnie w systemie Mac OS 10.10.3. Wyprowadza „{375..3500..5}”.
zjadł
6
@aswine dlatego zawsze pytaj o system operacyjny. Zwłaszcza, że ​​OSX ma wiele różnic zarówno od innych UNICE, jak i od Linuksa.
terdon
2
To nie jest różnica w systemie operacyjnym. To różnica wersji. OS X ma tylko BASH 3 OOTB. Możesz zainstalować BASH z tego wieku, używając prawie dowolnego menedżera pakietów OS X. Na przykład używam Home Brew.
kojiro
2
Fakt, że rozszerzenia nie działają bezpośrednio, ale skutecznie bash -cgwarantuje , że w grę wchodzą dwie różne powłoki. Czy $SHELL --versionmówi, że to bash 3?
dhag
3
@mikeserv Odpowiedź jest bardzo prosta - nie napisałem tego, ponieważ nie miałem pojęcia, w której wersji bashtej funkcji została wprowadzona. Nauczyłem się tego sam z komentarza Kojiro. I proszę, nie porównujcie mnie ze SC, naprawdę nie znam wszystkich szczegółów i historii wszystkich pocisków od końca 1970 roku. Jeszcze raz - Jeśli się tego spodziewacie, proszę o głosowanie.
jimmij
17

Korzystanie z SEQ (1)

for i in $(seq 375 5 3500)
do
    echo $i
done

Lub po prostu:

seq 375 5 3500
Emeric
źródło
4
Jeszcze łatwiej byłoby porzucić pętlę i po prostu użyć seq 375 5 3500.
dhag
11

Twój fragment for-loop nie działał tak, jak potrzebujesz z dwóch powodów:

  • (($i += 5))- tutaj $ijest rozwinięty do wartości i. Zatem rozwinięcie będzie podobne ((375 += 5)), co nie ma sensu (próba przypisania liczby literalnej do innej liczby literalnej). Zwykle można to osiągnąć za pomocą ((i += 5))(nie, $aby rozwinąć zmienną)
  • {375..3500}Zostanie rozszerzony przed pierwszej iteracji pętli. To będzie lista liczb 375 376 ... 3499 3500. Do każdej iteracji pętli izostaną przypisane do każdego z tych numerów, jeden po drugim. Zatem na początku każdej iteracji izostanie przypisany do następnej wartości na tej liście, licząc w krokach co 1. ((i += 5))Skutecznie nic nie robi - dodaje 5 do i, ale następnie ponownie przypisuje się na początku następna iteracja.

Myślę, że najbardziej podoba mi się ta for (( ; ; ))odpowiedź, ale oto kilka alternatywnych sposobów na zastanowienie się:


Ponieważ mamy do czynienia z wielokrotnościami 5, a {a..b..i}rozszerzenie nie jest obsługiwane w wersji bash 3.2.57 (1) (w OS X), możemy zamiast tego zrobić tę nieco tajemniczą rzecz:

for i in 375 {38..349}{0,5} 3500; do
    echo $i
done

To pokazuje, jak można użyć bash do stworzenia produktu kartezjańskiego.


Myślę, że generalnie pętla for jest najwygodniejszym sposobem, aby to zrobić, ale jeśli jesteś zainteresowany, możesz użyć programu pętli while (nieco bliżej BASIC):

count=375
while (( count <= 3500 )); do
    echo $count
    (( count += 5 ))
done
Cyfrowa trauma
źródło
1
@jimmij ... co to jest produkt kartezjański? możesz zagłosować za komentarzem, jeśli chcesz, a ja nawet nie będzie mnie to obchodzić, jeśli mi powiesz. Jestem ciekawy.
mikeserv
1
@mikeserv nie możesz zlekceważyć komentarza: en.wikipedia.org/wiki/Cartesian_product
jimmij
@jimmij - jeśli tak, to nadal ze mną w porządku.
mikeserv
@jimmij Diabeł, o którym mówisz? Produkt kartezjański to zestaw krotek każdej możliwej kombinacji elementów z dwóch zestawów (które mogą, ale nie muszą być tym samym zestawem). Mówiąc bardziej kolokwialnie, to tylko „wszystkie możliwe kombinacje” z dwóch grup rzeczy. Widzę tylko jeden zestaw. Jak wygląda produkt kartezjański?
jpmc26,
1
@ jpmc26 Masz rację, są to „wszystkie możliwe kombinacje”, więc wykreślmy dwie osie - najpierw napisz wszystkie liczby od 38kasy 349. Na sekundę tylko dwie liczby: 0i 5. Teraz pozwala tworzyć zamówionych par tych wszystkich kombinacjach - można zapisać je jako zestawy: {38,0}, {38,5}... {349,5}, lub po prostu usunąć zbędne symbole {, ,a }i dostać ... nowe numery 380... 3495.
jimmij
5

Chociaż istnieje oczywiście aplikacja do tego ( seq 375 5 3500), istnieją różne sposoby wykonania tego z wiersza poleceń. Chociaż najszybsza i najprostsza będzie po prostu używać seq, oto kilka innych opcji:

for i in {375..3500}; do [[ (($i % 5)) -eq 0 ]] && echo $i; done

i=370; while [ $i -le 3500 ]; do printf "%s\n" $((i+=5)); done

perl -le '$i=shift;while($i<=$ARGV[0]){print $i; $i+=5; }' 375 3500
perl -le 'map{print $_ + 5} 370..3495'

awk 'BEGIN{ for(i=375;i<=3500;i+=5){print i}}'
terdon
źródło
Nit pick: $(($i % 5))można napisać $((i % 5)).
G-Man mówi „Przywróć Monikę”
4

POSIXly:

i=370
while [ 3500 -gt "$i" ]
do    echo "$((i+=5))"
done

...lub...

echo 'for(x=370;x<=3500;x+=5)x' |bc

Nie wiem, dlaczego zrobiłbyś to w inny sposób. Z wyjątkiem oczywiście ...

seq 375 5 3500

... lub z dc:

echo '370[5+pd3500>p]splpx'|dc
mikeserv
źródło
Jestem ciekawy, jak dcdziała kod. To z pewnością bardziej intrygujące niż używanie seq.
dhag
1
@dhag - zapisuje małą pętlę. to saves się [ciąg ]do górnej części ptablicy, a następnie lOAD z powrotem na górę stosu i e xecutes go jako makro. W ramach makra wciskamy stos, a następnie wrzucamy 5go, a drugi od góry i +one, zastępując obie wartości stosu ich sumą, która jest pzrewidowana i dzastosowana, zanim 3500zostanie wypchnięta na stos, kiedy go pop i dupe, które właśnie stworzyliśmy porównanie >. Jeśli 3500jest większy, ładujemy i wykonujemy jako makro ciąg przechowywany w ptablicy (nasze bieżące makro) , lub jeśli nie jest zepsuty.
mikeserv
3

Jeśli utkniesz na Bash 3:

echo {375..3500} | tr ' ' '\n' | sed -n 'p;n;n;n;n'

a jeśli wolisz awk:

echo {375..3500} | tr ' ' '\n' | awk '!((NR-1)%5)'

Nie wiedziałem o rozszerzaniu nawiasów klamrowych - to naprawdę fajne.

bonh
źródło