In uno script bash shell, scrivendo un ciclo for che scorre i valori di stringa


24

In bash, so che è possibile scrivere un forciclo in cui una variabile di controllo del ciclo iscorre su interi specificati. Ad esempio, posso scrivere uno script di shell bash che stampa gli interi tra 1 e 10:

#!/bin/bash

for i in {1..10}
do
 echo $i
done

È possibile invece eseguire l'iterazione su una variabile di controllo del ciclo che è una stringa, se fornisco un elenco di stringhe? Ad esempio, supponiamo che io abbia una stringa fnameche rappresenta un nome di file. Voglio chiamare un set di comandi per ciascun nome di file. Ad esempio, potrei voler stampare il contenuto fnamedell'uso di un comando come questo:

#!/bin/bash

for fname in {"a.txt", "b.txt", "c.txt"}
do
 echo $fname
done

In altre parole, nella prima iterazione, fnamedovrebbe avere il valore fname="a.txt", mentre nella seconda iterazione, fnamedovrebbe avere il valore fname="b.txt"e così via. Sfortunatamente, sembra che la sintassi sopra non sia del tutto corretta. Vorrei ottenere l'output:

a.txt

b.txt

c.txt

ma quando provo il codice sopra, ottengo questo output:

{A.txt,

b.txt,

c.txt}

Potete per favore aiutarmi a determinare la sintassi corretta, in modo che io possa modificare in modo iterativo il valore / contenuto della variabile fname? Grazie per il tuo tempo.


5
Rimuovi {}, non hai bisogno di nulla per passare in rassegna un elenco (delimitato da spazi)
Mat

3
@Mat significa rimuovere la {} e la ,s. L'alternativa è rimuovere gli spazi. Quindi "a.txt" "b.txt" "c.txt"o {"a.txt","b.txt","c.txt"}. Ma preferisco {a..c}.txtinvece.
arte

Risposte:


39

La sintassi corretta è la seguente:

#!/bin/bash

for fname in a.txt b.txt c.txt
do
  echo $fname
done

9
Inoltre, supponendo una matrice di nomi fnames=( a.txt b.txt c.txt )è possibile utilizzare la sintassi for f in ${fnames[@]}; do echo $f; done.

1
È vero for fname in a.txt b.txt c.txte si for fname in "a.txt" "b.txt" "c.txt"ottengono risultati identici?
Andrew,

Andrew, sì, è vero. Produrranno risultati identici
Ali Gangji,

1
Ovviamente dovresti usare for f in "${fnames[@]}"; do echo $f; done(tra virgolette ${fnames[@]}) se i fnamesvalori potrebbero contenere spazi bianchi. E dovresti usare "$f", specialmente se stai facendo qualcosa di più sofisticato di echo(ad esempio, cato cp). (E anche se stai solo facendo echo, dovresti usare printfinvece.)
Scott,

Penso che @ user13742 dovrebbe essere notato anche nella risposta.
Fallenreaper

1

Mi sembra che dovresti semplicemente ...

printf %s.txt\\n a b c

Mi piace anche se probabilmente l'OP aveva in mente qualcosa da fare oltre all'eco prima che fosse fatto.
Elder Geek,

0

Come notato dall'utente13742 nei commenti , possiamo fare uso di array in bashe ksh:

#!/usr/bin/env bash

files_list=( "a.txt" "b.txt" "c and space.txt" )

for i in "${files_list[@]}"
do
    echo "$i"
    # do something else here,maybe
done

E funziona così:

$ ./iterate_files_array.sh                                                      
a.txt
b.txt
c and space.txt

Tuttavia, alcune shell come dash( /bin/shsu Ubuntu) non supportano gli array. In tal caso, potremmo ricorrere all'uso della struttura del documento qui:<<

#!/bin/sh

while IFS= read -r line
do
    echo "$line"
done << EOL
one.txt
two.txt
with space.txt
EOL
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.