Posso usare una variabile in un'espansione del controvento Bash?


10

Di seguito è riportato una sorta di pseudo-codice per ciò che sto cercando di realizzare:

#!/bin/bash

# I already have the variable below figured out (positive integer):
numlines=$([returns number of lines containing specific characters in a file])

# This is basically what I want to do with it:
for i in {1..$numlines}; do
    # the part below is already figured out as well:        
    do some other stuff
done

Posso eseguirlo bene dalla riga di comando inserendo il numero effettivo nella sequenza `{1..n} '. Devo solo sapere se è possibile includere una variabile qui e come procedere.

  • Ho cercato exporting esso
  • Ho provato a mettere la variabile stessa tra parentesi graffe all'interno della sequenza: {1..${numlines}}
  • Ho provato a metterlo tra virgolette sperando che si espandesse: {1.."$numlines"}
  • Ho provato a sfuggire a $:{1..\$numlines}

Devo usare un set -[something]comando per espandere questa variabile? Ho anche provato alcune forme di utilizzo eval... tutto inutilmente.

Devo solo sapere se c'è qualcosa di semplice o oscuro che mi manca o se questo è persino possibile prima di perdere altro tempo.

Potrei mettere insieme un modo davvero molto hacker di farlo per farlo funzionare come necessario, ma mi piacerebbe evitarlo, se possibile, e imparare il modo giusto per farlo.


Risposte:


11

Sfortunatamente, non c'è modo di usare una variabile in quell'espansione (AFAIK), poiché l'espansione variabile avviene dopo l'espansione del controvento.

Fortunatamente, c'è uno strumento che fa lo stesso lavoro.

for i in $(seq 1 $numlines); do
    # stuff
done

seqproviene dai coreutils GNU; non ho idea di come farlo in POSIX.


1
(Più 1). seqè buono per i sistemi GNU e, se ricordo bene, il più recente OSX. Su altri sistemi BSD, si può usare invece jot .
Giovanni 1024

seqfunziona perfettamente. Grazie mille per la tua pronta risposta.
rubynorails

Questo non faceva parte della mia domanda, ma qual è la sintassi (se presente) per farlo in ordine inverso, come {16..1}? $(seq $numlines 1)non ha funzionato. Immagino di poter sempre man seq, ma mi chiedevo solo se qualcuno sapesse dalla testa.
rubynorails,

1
Ho appena scoperto come farlo al contrario da questo link -for i in $(seq $numlines -1 1)
rubynorails,

seq ${numlines} -1 0
DopeGhoti,

10

Sicuro. Se si desidera un ciclo for che incrementa una variabile intera, utilizzare la forma del forciclo che incrementa una variabile intera (o più generalmente esegue l'aritmetica sulle variabili del ciclo).

for ((i=1; i<=numlines; i++)); do  done

Questo costrutto funziona in bash (e ksh93 e zsh), ma non in semplice sh. In semplice sh, usa un ciclo while e il [ … ]costrutto test ( ).

i=1
while [ "$i" -le "$numlines" ]; do
  
  i=$((i+1))
done

8

Se devi evitare seq, che come sottolinea Tom Hunt sembra essere la solita soluzione a questo, allora evalsicuramente puoi farlo (però, non lo incoraggerei):

eval 'for i in {1..'$numlines'}; do echo $i; done'

Puoi rimanere POSIX evitando l'espansione {} e semplicemente fare confronti matematici e interi su $numlines:

while [ ! "$numlines" -eq 0 ]; do
     echo "$numlines"
     : $((numlines-=1))
done

Al di fuori di POSIX, bashed kshe zshsono anche in stile C forloop:

for((i=0; i<numlines; i++)); do echo $i; done

1
Apprezzo molto anche questa risposta. Mentre ha seqfunzionato bene per il mio scenario e sembrava essere la soluzione più semplice, è bene sapere che ci sono altre (anche POSIX) alternative. Grazie per questo.
rubynorails,

2
Non c'è davvero alcun motivo per usare eval; se hai un'espansione di parentesi graffe, hai il loop in stile C.
Chepner,

@PSkocik - Se potessi scegliere 2 risposte, sceglierei anche questa. Quando mi sono imbattuto nel fatto che avevo bisogno di farlo al contrario, il tuo evalesempio è stato il più semplice e mi avrebbe salvato dal dover cercare un modo alternativo di farlo usando seq. Il whileciclo è un po 'ingombrante per me. Mi piace mantenere le cose brevi e dolci, e non potrei mai far funzionare il forciclo in stile C dando iil valore di 0 o 1. Non è mai tornato correttamente ed era sempre un po 'fuori. Sono sicuro che potrebbe essere ottimizzato per funzionare correttamente, ma si tratta comunque di soluzioni sicuramente utili.
rubynorails,

L' evalapproccio è problematico se c'è qualcosa di non banale all'interno del corpo del loop. Immagino che non sarebbe molto leggibile se avessi bisogno di nidificare due di questi anelli.
Kasperd,
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.