Dans un script shell bash, écriture d'une boucle for qui itère sur les valeurs de chaîne

24

En bash, je sais qu'il est possible d'écrire une forboucle dans laquelle une variable de contrôle de boucle iitère sur des entiers spécifiés. Par exemple, je peux écrire un script shell bash qui imprime les entiers entre 1 et 10:

#!/bin/bash

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

Est-il possible d'itérer à la place une variable de contrôle de boucle qui est une chaîne, si je fournis une liste de chaînes? Par exemple, supposons que j'ai une chaîne fnamequi représente un nom de fichier. Je veux appeler un ensemble de commandes pour chaque nom de fichier. Par exemple, je pourrais vouloir imprimer le contenu de l' fnameutilisation d'une commande comme celle-ci:

#!/bin/bash

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

En d'autres termes, à la première itération, fnamedevrait avoir la valeur fname="a.txt", tandis qu'à la deuxième itération, fnamedevrait avoir la valeur fname="b.txt", et ainsi de suite. Malheureusement, il semble que la syntaxe ci-dessus ne soit pas tout à fait correcte. Je voudrais obtenir la sortie:

a.txt

b.txt

c.txt

mais quand j'essaye le code ci-dessus, j'obtiens cette sortie:

{a.txt,

b.txt,

c.txt}

Pouvez-vous m'aider à déterminer la syntaxe correcte, afin que je puisse modifier de manière itérative la valeur / le contenu de la variable fname? Merci pour votre temps.

Andrew
la source
5
Supprimez le {}, vous n'avez besoin de rien pour parcourir une liste (délimitée par des espaces)
Mat
3
@Mat signifie supprimer le {} et le ,s. L'alternative est de supprimer les espaces. Alors soit "a.txt" "b.txt" "c.txt"ou {"a.txt","b.txt","c.txt"}. Mais je préfère {a..c}.txtplutôt.
manatwork

Réponses:

39

La syntaxe correcte est la suivante:

#!/bin/bash

for fname in a.txt b.txt c.txt
do
  echo $fname
done
Ali Gangji
la source
9
En supposant également un tableau de noms, fnames=( a.txt b.txt c.txt )vous pouvez utiliser la syntaxe for f in ${fnames[@]}; do echo $f; done.
1
Est - il vrai que , for fname in a.txt b.txt c.txtet for fname in "a.txt" "b.txt" "c.txt"donner des résultats identiques?
Andrew
Andrew, oui c'est vrai. Ils donneront des résultats identiques
Ali Gangji
1
Bien sûr, vous devez utiliser for f in "${fnames[@]}"; do echo $f; done(avec des guillemets autour ${fnames[@]}) si les fnamesvaleurs peuvent contenir des espaces. Et vous devriez utiliser "$f", surtout si vous faites quelque chose de plus sophistiqué que echo(par exemple, catou cp). (Et même si vous ne faites que cela echo, vous devriez utiliser à la printfplace.)
Scott
Je pense que @ user13742 devrait également être noté dans la réponse.
Fallenreaper
1

Il me semble que tu devrais juste faire ...

printf %s.txt\\n a b c
mikeserv
la source
J'aime ça même si le PO avait probablement autre chose en tête que d'écho avant qu'il n'ait fini.
Elder Geek
0

Comme indiqué par user13742 dans les commentaires , nous pouvons utiliser des tableaux dans bashet 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

Et fonctionne ainsi:

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

Cependant, certains shells tels que dash( /bin/shsur Ubuntu) ne prennent pas en charge les tableaux. Dans ce cas, nous pourrions recourir à la structure du document ici:<<

#!/bin/sh

while IFS= read -r line
do
    echo "$line"
done << EOL
one.txt
two.txt
with space.txt
EOL
Sergiy Kolodyazhnyy
la source