2012-06-08 13 views
15

Diciamo che avete un semplice cicloBash loop, stampa corrente iterazione?

while read line 
do 
    printf "${line#*//}\n" 
done < text.txt 

C'è un modo elegante di stampare l'iterazione corrente con l'uscita? Qualcosa come

 
0 The 
1 quick 
2 brown 
3 fox 

Spero di evitare di impostare una variabile e di incrementarla su ogni ciclo.

risposta

21

Per fare questo, si avrebbe bisogno di incrementare un contatore su ogni iterazione (come se si cerchi di evitare).

count=0 
while read -r line; do 
    printf '%d %s\n' "$count" "${line*//}" 
    ((count++)) 
done < test.txt 

EDIT: Dopo un po 'di più il pensiero, lo si può fare senza un contatore se si dispone di bash versione 4 o superiore:

mapfile -t arr < test.txt 
for i in "${!arr[@]}"; do 
    printf '%d %s' "$i" "${arr[i]}" 
done 

L'incorporato mapfile legge l'intero contenuto del file nella matrice . È quindi possibile eseguire iterazioni sugli indici dell'array, che saranno i numeri di riga e accedere a quell'elemento.

+2

Bella soluzione, anche se 'mapfile' può essere terribilmente affamato di memoria se hai a che fare con file di grandi dimensioni.Senza conoscere la dimensione del file dell'OP, raccomanderei di andare con qualcosa che viene eseguito attraverso una pipe, e quindi è un file indipendente agnostico. – ghoti

0

Aggiornamento: Altre risposte pubblicate qui sono migliori, specialmente quelle di @Graham e @DennisWilliamson.

Qualcosa di molto simile a questo dovrebbe adattarsi:

tr -s ' ' '\n' <test.txt | nl -ba 

È possibile aggiungere un flag -v0 al comando nl se si desidera l'indicizzazione da 0.

+0

Abbastanza corretto. Allora, il comando 'nl' sarebbe adatto? – thb

+0

Non ho un comando 'n1' in FreeBSD, OSX o Illumos. Da dove viene? – Graham

+1

Ah - fastidiosi caratteri monospace. @thb, penso che avresti ancora bisogno di qualcosa per togliere le righe a '//', come '$ {line # * //}' fa. Ma potreste certamente passare il ciclo while a 'nl', proprio come ho fatto con' grep -n .' nella mia risposta. – Graham

1
n=0 
cat test.txt | while read line; do 
    printf "%7s %s\n" "$n" "${line#*//}" 
    n=$((n+1)) 
done 

Questo funzionerà anche nella shell Bourne.

Se si vuole davvero evitare incrementare una variabile, è possibile reindirizzare l'output attraverso grep o awk:

cat test.txt | while read line; do 
    printf " %s\n" "${line#*//}" 
done | grep -n . 

o

awk '{sub(/.*\/\//, ""); print NR,$0}' test.txt 
+0

Modificato per includere un'opzione per i criteri finali. – Graham

+0

Preferisco la soluzione awk, anche se 'sub (/.*\/\//," ");' è * non * uguale a '$ {line # * //}' – ghoti

+1

Beh ... la prima parte della tua risposta è praticamente identica a quella di Jordan, e le altre due sembrano funzionare bene. Chalk fino alla casualità di StackOverflow. :-) – ghoti

13

Spesso non si vede, ma è possibile avere più comandi nella clausola di condizione di un ciclo while. Quanto segue richiede ancora una variabile contatore esplicita, ma la disposizione può essere più adatta o attraente per alcuni usi.

while ((i++)); read -r line 
do 
    echo "$i $line" 
done < inputfile 

La condizione while è soddisfatta da qualunque ultimi comando restituisce (read in questo caso).

Alcune persone preferiscono includere lo do sulla stessa riga. Ecco come sarebbe:

while ((i++)); read -r line; do 
    echo "$i $line" 
done < inputfile 
+2

+1 Non sapevo mentre la sintassi lo permetteva. – jordanm

+4

@jordanm: 'for' fa qualcosa di simile:' for ((i = 0, j = 14; i <= 20; i ++, j + = 11)) '. Inoltre, puoi leggere due (o più) righe da un file alla volta: 'while read -r oddline; leggi -r anche linea; do echo "dispari: [$ oddline] pari a: [$ evenline]"; fatto

Problemi correlati