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.