Come posso creare una sceneggiatura da contare per cinque?


10

Stavo cercando di creare uno script bash molto semplice per elencare tutti i multipli di cinque tra 375 e 3500 (375, 380, 385 ...). Una cosa che ho provato e non ha funzionato è:

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

Mi sono arreso dopo un po 'e l'ho scritto in BASIC in circa 15 secondi:

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

Come posso creare il mio programma BASIC in uno script bash?


questo è meglio in caso di overflow dello stack: Unix e Linux riguardano il sistema operativo, non la codifica. La codifica è per StackOverflow.
noɥʇʎԀʎzɐɹƆ

Risposte:


20

In alternativa è possibile utilizzare uno stile C tradizionale per loop:

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

Questo è forse meno chiaro dell'uso di seq, ma non genera alcun sottoprocesso. Sebbene dal momento che ho familiarità con C, non avrei alcuna difficoltà a capirlo, ma YMMV.


1
L'aspetto negativo di questo metodo è che è meno portatile. Il metodo seq funziona in POSIX sh.
Wouter Verhelst,

Oh, lo stile C per i loop non esiste in POSIX sh? Ogni giorno impari qualcosa di nuovo ...
Muzer,

2
@WouterVerhelst Bene, tranne se sei limitato a POSIX sh, probabilmente non avrai seq, che fa parte dei coreutils GNU. busybox ha un'implementazione più limitata, ma a parte questo, molti sistemi embedded probabilmente non lo avranno affatto.
Chris Down,

1
@Muzer - almeno dashnon lo supporta. @ChrisDown - Non c'è motivo per cui "POSIX sh" debba implicare "no seq". Ma sì, garantito, avevo trascurato il fatto che seq non è specificato da POSIX.
Wouter Verhelst,

27

Dato che usi comunque l'espansione parentesi graffa, quindi utilizza pienamente la sua funzione:

echo {375..3500..5}

È inoltre possibile utilizzare questa tecnica per stampare ogni numero in linea separata con testo opzionale utilizzando printf, invece di echo, ad esempio:

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

modificare

Come sottolineato da @kojiro nel commento, Mac OS utilizza bash 3 come shell predefinita, che non supporta l'incremento nell'espressione in sequenza dell'espansione del controvento. È necessario eseguire l'aggiornamento alla versione 4 di bash o utilizzare un'altra shell che supporti tale (ad esempio zsh recente).


2
Questo non funziona per me su Mac OS 10.10.3. Emette "{375..3500..5}".
arrivo il

6
@aswine è per questo che dovresti sempre menzionare il tuo sistema operativo quando chiedi. Soprattutto dal momento che OSX presenta una serie di differenze sia rispetto ad altri UNICE che a Linux.
terdon

2
Questa non è una differenza del sistema operativo in sé. È una differenza di versione. OS X ha solo BASH 3 OOTB. Puoi installare un BASH di questo secolo usando quasi tutti i gestori di pacchetti OS X. Uso Home Brew, per uno.
Kojiro,

2
Il fatto che l'espansione fallisca direttamente ma riesce bash -cpraticamente a garantire che siano coinvolte due diverse shell. Fa $SHELL --versiondire che è bash 3?
dhag,

3
@mikeserv La risposta è molto semplice: non l'ho scritto perché non avevo idea di quale versione di bashquesta funzionalità fosse stata introdotta. L'ho imparato io stesso dal commento di Kojiro. E per favore non confrontarmi con SC, davvero non conosco tutti i dettagli e la storia di tutte le shell dalla fine del 1970. Ancora una volta - Se te lo aspetti, per favore, per favore, riduci il voto.
Jimmij,

17

Utilizzando SEQ (1)

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

O semplicemente:

seq 375 5 3500

4
Ancora più semplice sarebbe lasciar perdere il loop e usarlo seq 375 5 3500.
dhag,

11

Lo snippet for-loop non ha funzionato come richiesto per due motivi:

  • (($i += 5))- qui $iviene espanso al valore di i. Quindi l'espansione sarà qualcosa di simile ((375 += 5)), che non ha senso (tentare di assegnare un numero letterale a un altro numero letterale). Questo sarebbe normalmente ottenuto con ((i += 5))(no $per espandere la variabile)
  • Il {375..3500}verrà espanso prima della prima iterazione del loop. Sarà la lista dei numeri 375 376 ... 3499 3500. Per ogni iterazione del ciclo, iverrà assegnato a ciascuno di questi numeri, uno per uno. Quindi all'inizio di ogni iterazione, iverrà riassegnato al valore successivo in quell'elenco, contando in passi di 1. L' ((i += 5))effettivo non fa nulla - aggiunge 5 a i, ma poi sono appena riassegnato all'inizio del prossima iterazione.

Penso che la for (( ; ; ))risposta mi piaccia di più, ma ecco alcune alternative per farti pensare:


Dato che abbiamo a che fare con multipli di 5 e l' {a..b..i}espansione non è supportata nella versione bash 3.2.57 (1) (in OS X), possiamo invece fare questa cosa leggermente arcana:

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

Questo dimostra come bash può essere usato per creare un prodotto cartesiano.


Penso che in generale un ciclo for sia il modo più conveniente per farlo, ma se sei interessato, puoi usare un programma while-loop (un po 'più vicino al tuo BASIC):

count=375
while (( count <= 3500 )); do
    echo $count
    (( count += 5 ))
done

1
@jimmij ... cos'è un prodotto cartesiano? puoi sottovalutare il commento se vuoi, e non mi importerebbe nemmeno se me lo dici. Sono curioso.
Mikeserv,

1
@mikeserv non puoi sottovalutare il commento: en.wikipedia.org/wiki/Cartesian_product
jimmij

@jimmij - è ancora ok con me se lo fai.
Mikeserv,

@jimmij Di che diavolo stai parlando? Un prodotto cartesiano è un insieme di tuple di ogni possibile combinazione di elementi di due serie (che possono essere o meno la stessa serie). Più colloquialmente, sono solo "tutte le possibili combinazioni" di due gruppi di cose. Vedo solo un set. Come c'è un prodotto cartesiano qui?
jpmc26,

1
@ jpmc26 Hai ragione, sono "tutte le combinazioni possibili", quindi lascia tracciare due assi: prima scrivi tutti i numeri da 38fino a 349. Al secondo solo due numeri: 0e 5. Ora lascia creare coppie ordinate di quelle tutte le combinazioni - si possono scrivere come un set: {38,0}, {38,5}... {349,5}, o semplicemente rimuovere i simboli ridondanti {, ,ed }e ottenere nuovi numeri ... 380... 3495.
Jimmij,

5

Mentre esiste, ovviamente, un'app per that ( seq 375 5 3500), ci sono vari modi per farlo dalla riga di comando. Mentre il più semplice e veloce verrà utilizzato seq, ecco alcune altre opzioni:

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

Nit pick: $(($i % 5))può essere scritto $((i % 5)).
G-Man dice "Ripristina Monica" il

4

POSIXly:

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

...o...

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

Non so perché lo faresti in qualsiasi altro modo. Tranne, ovviamente ...

seq 375 5 3500

... o con dc:

echo '370[5+pd3500>p]splpx'|dc

Sono curioso di sapere come funziona il dccodice. È sicuramente più intrigante dell'uso seq.
dhag,

1
@dhag: scrive un piccolo ciclo. esso saves la [stringa ]all'inizio della pmatrice, quindi lOADs indietro sulla parte superiore della pila ed e xecutes come una macro. All'interno della macro spingiamo 5sullo stack, quindi pop e la seconda dall'alto e +loro, sostituendo entrambi i valori dello stack con la loro somma, che viene rintanato pe duplicated prima che 3500venga inserito nello stack, quando lo pop e il dupe abbiamo appena creato confronto >. Se 3500è maggiore, cariciamo ed eseguiamo come macro la stringa memorizzata pnell'array (la nostra macro corrente) , o se non la spezziamo.
Mikeserv,

3

Se sei bloccato su Bash 3:

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

e se preferisci awk:

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

Non sapevo dell'espansione del tutore: è davvero bello.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.