Faire une boucle à travers un tableau de chaînes dans Bash?


1503

Je veux écrire un script qui boucle sur 15 chaînes (tableau éventuellement?) Est-ce possible?

Quelque chose comme:

for databaseName in listOfNames
then
  # Do something
end

Réponses:


2392

Vous pouvez l'utiliser comme ceci:

## declare an array variable
declare -a arr=("element1" "element2" "element3")

## now loop through the above array
for i in "${arr[@]}"
do
   echo "$i"
   # or do whatever with individual element of the array
done

# You can access them using echo "${arr[0]}", "${arr[1]}" also

Fonctionne également pour la déclaration de tableau multiligne

declare -a arr=("element1" 
                "element2" "element3"
                "element4"
                )

778

C'est possible, bien sûr.

for databaseName in a b c d e f; do
  # do something like: echo $databaseName
done 

Voir Boucles Bash pour, pendant et jusqu'à pour plus de détails.


18
Quel est le problème avec cette approche? Dans les cas simples, cela semble fonctionner et est donc plus intuitif que la réponse de @ anubhava.
Dr Jan-Philip Gehrcke

17
Cela fonctionne particulièrement bien avec la substitution de commandes, par exemple for year in $(seq 2000 2013).
Brad Koch

23
Le problème est qu'il a demandé à propos de l'itération à travers un tableau.
mgalgs

20
L'approche 'declare' fonctionne mieux si vous devez parcourir le même tableau à plusieurs endroits. Cette approche est plus propre mais moins flexible.
StampyCode

15
Pourquoi n'est-ce pas n ° 1? C'est plus propre et vous pouvez facilement réutiliser le tableau simplement en définissant une chaîne, c'est-à-dire DATABASES="a b c d e f".
Nerdmaster

201

Aucune de ces réponses ne comprend un compteur ...

#!/bin/bash
## declare an array variable
declare -a array=("one" "two" "three")

# get length of an array
arraylength=${#array[@]}

# use for loop to read all values and indexes
for (( i=1; i<${arraylength}+1; i++ ));
do
  echo $i " / " ${arraylength} " : " ${array[$i-1]}
done

Production:

1  /  3  :  one
2  /  3  :  two
3  /  3  :  three

7
Cela devrait être la réponse acceptée, est la seule qui fonctionne lorsque les éléments du tableau contiennent des espaces.
jesjimher

1
C'est juste pour la sortie de l'exemple avec un compteur. Il est également assez trivial de changer cela, et cela fonctionne tout de même.
caktux

7
L'écho à la fin est buggé. Vous n'avez pas besoin de citer des constantes, vous devez citer des extensions, ou vous pouvez simplement citer les deux comme suit: echo "$i / ${arraylength} : ${array[$i-1]}"- sinon, si votre $icontient un glob, il sera développé, s'il contient un onglet, il sera changé en un espace, etc.
Charles Duffy

10
@bzeaman, bien sûr - mais si vous êtes bâclé à propos de telles choses, cela nécessite une analyse contextuelle (comme vous venez de le faire) pour prouver quelque chose de correct, et une nouvelle analyse si ce contexte change ou si le code est réutilisé dans un endroit différent ou pour quelle que soit la raison, un flux de contrôle inattendu est possible. Écrivez-le de manière robuste et c'est correct quel que soit le contexte.
Charles Duffy

5
Cela ne fonctionnera pas pour un tableau clairsemé, c'est-à-dire s'il manque des éléments dans le tableau. Par exemple, si vous avez des éléments de tableau A [1] = 'xx', A [4] = 'yy' et A [9] = 'zz', la longueur sera 3 et la boucle ne traitera pas tous les éléments .
M. Ed

146

Oui

for Item in Item1 Item2 Item3 Item4 ;
  do
    echo $Item
  done

Production:

Item1
Item2
Item3
Item4

Pour préserver les espaces; entrées de liste de devis simples ou doubles et extensions de liste de devis doubles.

for Item in 'Item 1' 'Item 2' 'Item 3' 'Item 4' ;
  do
    echo "$Item"
  done

Production:

Item 1
Item 2
Item 3
Item 4

Pour créer une liste sur plusieurs lignes

for Item in Item1 \
            Item2 \
            Item3 \
            Item4
  do
    echo $Item
  done

Production:

Item1
Item2
Item3
Item4


Variable de liste simple

List=( Item1 Item2 Item3 )

ou

List=(
      Item1 
      Item2 
      Item3
     )

Affichez la variable de liste:

echo ${List[*]}

Production:

Item1 Item2 Item3

Parcourez la liste:

for Item in ${List[*]} 
  do
    echo $Item 
  done

Production:

Item1
Item2
Item3

Créez une fonction pour parcourir une liste:

Loop(){
  for item in ${*} ; 
    do 
      echo ${item} 
    done
}
Loop ${List[*]}

Utiliser le mot clé declare (commande) pour créer la liste, qui est techniquement appelée un tableau:

declare -a List=(
                 "element 1" 
                 "element 2" 
                 "element 3"
                )
for entry in "${List[@]}"
   do
     echo "$entry"
   done

Production:

element 1
element 2
element 3

Création d'un tableau associatif. Un dictionnaire:

declare -A continent

continent[Vietnam]=Asia
continent[France]=Europe
continent[Argentina]=America

for item in "${!continent[@]}"; 
  do
    printf "$item is in ${continent[$item]} \n"
  done

Production:

 Argentina is in America
 Vietnam is in Asia
 France is in Europe

Variables ou fichiers CVS dans une liste .
Changer le séparateur de champ interne d'un espace à ce que vous voulez.
Dans l'exemple ci-dessous, il est remplacé par une virgule

List="Item 1,Item 2,Item 3"
Backup_of_internal_field_separator=$IFS
IFS=,
for item in $List; 
  do
    echo $item
  done
IFS=$Backup_of_internal_field_separator

Production:

Item 1
Item 2
Item 3

Si besoin de les numéroter:

` 

cela s'appelle une tique arrière. Mettez la commande à l'intérieur des tiques arrière.

`commend` 

Il se trouve à côté du numéro un de votre clavier et ou au-dessus de la touche de tabulation. Sur un clavier standard anglais américain.

List=()
Start_count=0
Step_count=0.1
Stop_count=1
for Item in `seq $Start_count $Step_count $Stop_count`
    do 
       List+=(Item_$Item)
    done
for Item in ${List[*]}
    do 
        echo $Item
    done

La sortie est:

Item_0.0
Item_0.1
Item_0.2
Item_0.3
Item_0.4
Item_0.5
Item_0.6
Item_0.7
Item_0.8
Item_0.9
Item_1.0

Se familiariser avec le comportement des bashes:

Créer une liste dans un fichier

cat <<EOF> List_entries.txt
Item1
Item 2 
'Item 3'
"Item 4"
Item 7 : *
"Item 6 : * "
"Item 6 : *"
Item 8 : $PWD
'Item 8 : $PWD'
"Item 9 : $PWD"
EOF

Lire le fichier de liste dans une liste et afficher

List=$(cat List_entries.txt)
echo $List
echo '$List'
echo "$List"
echo ${List[*]}
echo '${List[*]}'
echo "${List[*]}"
echo ${List[@]}
echo '${List[@]}'
echo "${List[@]}"

Manuel de référence de la ligne de commande BASH: signification spéciale de certains caractères ou mots pour le shell.


7
C'EST FAUX. Doit "${List[@]}"être correct, avec les guillemets . ${List[@]}est faux. ${List[*]}est faux. Essayez List=( "* first item *" "* second item *" )- vous obtiendrez un comportement correct pour for item in "${List[@]}"; do echo "$item"; done, mais pas à partir d'une autre variante.
Charles Duffy

1
Oui, les caractères spéciaux sont interprétés, ce qui peut être souhaitable ou non. J'ai mis à jour la réponse pour l'inclure.
FireInTheSky

2
Je suggérerais toujours fortement de montrer d' abord l' approche plus correcte / robuste . Les gens prennent souvent la première réponse qui semble fonctionner; si cette réponse comporte des mises en garde cachées, ils ne peuvent s'exposer que plus tard. (Il est non seulement wildcards qui sont brisées par le manque de citer, List=( "first item" "second item" )sera divisé en first, item, second, itemet).
Charles Duffy

Vous pouvez également envisager d'éviter d'utiliser un exemple qui pourrait conduire les gens à analyser la lssortie, en violation des meilleures pratiques .
Charles Duffy

1
Vous réfutez des affirmations que je n'ai jamais faites. Je suis assez familier avec la sémantique des citations de bash - voir stackoverflow.com/tags/bash/topusers
Charles Duffy

108

Dans le même esprit que la réponse de 4ndrew:

listOfNames="RA
RB
R C
RD"

# To allow for other whitespace in the string:
# 1. add double quotes around the list variable, or
# 2. see the IFS note (under 'Side Notes')

for databaseName in "$listOfNames"   #  <-- Note: Added "" quotes.
do
  echo "$databaseName"  # (i.e. do action / processing of $databaseName here...)
done

# Outputs
# RA
# RB
# R C
# RD

B. Aucun espace dans les noms:

listOfNames="RA
RB
R C
RD"

for databaseName in $listOfNames  # Note: No quotes
do
  echo "$databaseName"  # (i.e. do action / processing of $databaseName here...)
done

# Outputs
# RA
# RB
# R
# C
# RD

Remarques

  1. Dans le deuxième exemple, l'utilisation listOfNames="RA RB R C RD"a la même sortie.

D'autres moyens de saisir des données incluent:

Lire depuis stdin

# line delimited (each databaseName is stored on a line)
while read databaseName
do
  echo "$databaseName"  # i.e. do action / processing of $databaseName here...
done # <<< or_another_input_method_here
  1. le délimiteur bash IFS "séparateur de champ à la ligne" [ 1 ] peut être spécifié dans le script pour autoriser d'autres espaces (par exemple IFS='\n', ou pour MacOSIFS='\r' )
  2. J'aime aussi la réponse acceptée :) - J'ai inclus ces extraits comme d'autres moyens utiles qui répondent également à la question.
  3. Comprenant #!/bin/bash en haut du fichier de script indique l'environnement d'exécution.
  4. Il m'a fallu des mois pour comprendre comment coder cela simplement :)

Autres sources ( en boucle de lecture )


Cela crée l'impression que eol est utilisé comme séparateur de chaînes et, par conséquent, les espaces blancs sont autorisés dans les chaînes. Cependant, les chaînes avec des espaces sont encore séparées en sous-chaînes, ce qui est très très mauvais. Je pense que cette réponse stackoverflow.com/a/23561892/1083704 est meilleure.
Val

1
@Val, j'ai ajouté un commentaire de code avec une référence à IFS. (Pour tout le monde, IFSpermet de spécifier un délimiteur spécifique, ce qui permet aux autres espaces blancs d'être inclus dans les chaînes sans être séparés en sous-chaînes).
user2533809

7
Ça ne marche pas pour moi. $databaseNamecontient juste la liste entière, donc ne fait qu'une seule itération.
AlikElzin-kilaka

@ AlikElzin-kilaka Ma réponse ci-dessous résout ce problème afin que la boucle soit exécutée pour chaque ligne de la chaîne.
Jamie

43

Vous pouvez utiliser la syntaxe de ${arrayName[@]}

#!/bin/bash
# declare an array called files, that contains 3 values
files=( "/etc/passwd" "/etc/group" "/etc/hosts" )
for i in "${files[@]}"
do
    echo "$i"
done

31

Surpris que personne n'ait encore posté cela - si vous avez besoin des index des éléments pendant que vous parcourez le tableau, vous pouvez le faire:

arr=(foo bar baz)

for i in ${!arr[@]}
do
    echo $i "${arr[i]}"
done

Production:

0 foo
1 bar
2 baz

Je trouve cela beaucoup plus élégant que le style de boucle "traditionnel" (for (( i=0; i<${#arr[@]}; i++ )) ).

( ${!arr[@]}et $in'ont pas besoin d'être cités car ce ne sont que des chiffres; certains suggèrent de toute façon de les citer, mais ce n'est qu'une préférence personnelle.)


2
Cela devrait vraiment être la réponse choisie. 1: C'est simple et facile à lire. 2: Il gère correctement les espaces blancs, aucun non-sens IFS ne gêne. 3: Il gère correctement les tableaux clairsemés.
mrm

20

Ceci est également facile à lire:

FilePath=(
    "/tmp/path1/"    #FilePath[0]
    "/tmp/path2/"    #FilePath[1]
)

#Loop
for Path in "${FilePath[@]}"
do
    echo "$Path"
done

4
Celui-ci est clair et a fonctionné pour moi (y compris avec les espaces et la substitution de variables dans les éléments du tableau FilePath) uniquement lorsque j'ai défini la variable IFS correctement avant la définition du tableau FilePath: IFS=$'\n' cela peut également fonctionner pour d'autres solutions dans ce scénario.
Alan Forsyth

8

Tableau implicite pour le script ou les fonctions:

En plus de la bonne réponse de anubhava : Si la syntaxe de base pour la boucle est:

for var in "${arr[@]}" ;do ...$var... ;done

Il y a un cas particulier:

Lors de l' exécution d' un script ou une fonction, les arguments transmis à des lignes de commande seront affectés à la $@variable de tableau, vous pouvez accéder par $1, $2, $3et ainsi de suite.

Cela peut être rempli (pour test) par

set -- arg1 arg2 arg3 ...

Une boucle sur ce tableau pourrait s'écrire simplement:

for item ;do
    echo "This is item: $item."
  done

Notez que le travail réservé inn'est pas présent et pas de nom de tableau aussi!

Échantillon:

set -- arg1 arg2 arg3 ...
for item ;do
    echo "This is item: $item."
  done
This is item: arg1.
This is item: arg2.
This is item: arg3.
This is item: ....

Notez que c'est la même chose que

for item in "$@";do
    echo "This is item: $item."
  done

Puis dans un script :

#!/bin/bash

for item ;do
    printf "Doing something with '%s'.\n" "$item"
  done

Enregistrer dans un script myscript.sh, chmod +x myscript.shpuis

./myscript.sh arg1 arg2 arg3 ...
Doing something with 'arg1'.
Doing something with 'arg2'.
Doing something with 'arg3'.
Doing something with '...'.

Idem dans une fonction :

myfunc() { for item;do cat <<<"Working about '$item'."; done ; }

alors

myfunc item1 tiem2 time3
Working about 'item1'.
Working about 'tiem2'.
Working about 'time3'.

7
listOfNames="db_one db_two db_three"
for databaseName in $listOfNames
do
  echo $databaseName
done

ou juste

for databaseName in db_one db_two db_three
do
  echo $databaseName
done

7

Manière simple:

arr=("sharlock"  "bomkesh"  "feluda" )  ##declare array

len=${#arr[*]}  # it returns the array length

#iterate with while loop
i=0
while [ $i -lt $len ]
do
    echo ${arr[$i]}
    i=$((i+1))
done


#iterate with for loop
for i in $arr
do
  echo $i
done

#iterate with splice
 echo ${arr[@]:0:3}

6

Le tableau declare ne fonctionne pas pour le shell Korn. Utilisez l'exemple ci-dessous pour le shell Korn:

promote_sla_chk_lst="cdi xlob"

set -A promote_arry $promote_sla_chk_lst

for i in ${promote_arry[*]};
    do
            echo $i
    done

2
essayez le surligneur de code dans l'éditeur pour rendre votre code beau.
plongé

5
Bon à savoir, mais cette question concerne bash.
Brad Koch

1
Bugs Lotsa ici. Impossible d'avoir des entrées de liste avec des espaces, ne peut pas avoir des entrées de liste avec des caractères globaux. for i in ${foo[*]}est fondamentalement toujours la mauvaise chose - for i in "${foo[@]}"c'est la forme qui préserve les limites de la liste d'origine et empêche l'expansion des globes. Et l'écho doit êtreecho "$i"
Charles Duffy

4

Ceci est similaire à la réponse de user2533809, mais chaque fichier sera exécuté comme une commande distincte.

#!/bin/bash
names="RA
RB
R C
RD"

while read -r line; do
    echo line: "$line"
done <<< "$names"

3

Si vous utilisez le shell Korn, il y a " set -A databaseName ", sinon il y a " declare -a databaseName "

Pour écrire un script fonctionnant sur tous les shells,

 set -A databaseName=("db1" "db2" ....) ||
        declare -a databaseName=("db1" "db2" ....)
# now loop 
for dbname in "${arr[@]}"
do
   echo "$dbname"  # or whatever

done

Il devrait fonctionner sur tous les obus.


3
Non, ce n'est pas le cas: $ bash --versionGNU bash, version 4.3.33 (0) -release (amd64-portbld-freebsd10.0) $ set -A databaseName=("db1" "db2" ....) || declare -a databaseName=("db1" "db2" ....)bash: erreur de syntaxe près du jeton inattendu `('
Bernie Reiter

2

Essaye ça. Il fonctionne et a été testé.

for k in "${array[@]}"
do
    echo $k
done

# For accessing with the echo command: echo ${array[0]}, ${array[1]}

3
Cela ne fonctionne pas correctement. Essayez array=( "hello world" ), ou arrray=( "*" ); dans le premier cas, il imprimera helloet worldséparément, dans le second, il imprimera une liste de fichiers au lieu de*
Charles Duffy

6
... avant d'appeler quelque chose de "testé" dans le shell, assurez-vous de vérifier les cas d'angle, dont les espaces et les globes sont tous les deux.
Charles Duffy

2

Première ligne possible de chaque script / session Bash:

say() { for line in "${@}" ; do printf "%s\n" "${line}" ; done ; }

Utilisez par exemple:

$ aa=( 7 -4 -e ) ; say "${aa[@]}"
7
-4
-e

Peut considérer: echointerprète -ecomme option ici


2

Bouclage sur une seule ligne,

 declare -a listOfNames=('db_a' 'db_b' 'db_c')
 for databaseName in ${listOfNames[@]}; do echo $databaseName; done;

vous obtiendrez une sortie comme celle-ci,

db_a
db_b
db_c

2

Ce dont j'avais vraiment besoin, c'était quelque chose comme ça:

for i in $(the_array); do something; done

Par exemple:

for i in $(ps -aux | grep vlc  | awk '{ print $2 }'); do kill -9 $i; done

(Tuerait tous les processus avec vlc en leur nom)


1

Je passe en revue un tableau de mes projets pour une git pullmise à jour:

#!/bin/sh
projects="
web
ios
android
"
for project in $projects do
    cd  $HOME/develop/$project && git pull
end

7
Bien que cet extrait de code puisse résoudre la question, y compris une explication aide vraiment à améliorer la qualité de votre message. N'oubliez pas que vous répondrez à la question pour les lecteurs à l'avenir, et ces personnes pourraient ne pas connaître les raisons de votre suggestion de code. Essayez également de ne pas surcharger votre code avec des commentaires explicatifs, cela réduit la lisibilité du code et des explications!
kayess
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.