159 votes

Tableau Bash avec des espaces dans les éléments

Je suis en train de construire un tableau en bash des noms de fichiers de ma caméra:

FILES=(2011-09-04 21.43.02.jpg
2011-09-05 10.23.14.jpg
2011-09-09 12.31.16.jpg
2011-09-11 08.43.12.jpg)

Comme vous pouvez le voir, il y a un espace au milieu de chaque nom de fichier.

J'ai essayé d'entourer chaque nom de guillemets et d'échapper l'espace avec un slash inversé, aucun des deux ne fonctionne.

Lorsque j'essaie d'accéder aux éléments du tableau, il continue de traiter l'espace comme le délimiteur d'élément.

Comment puis-je capturer correctement les noms de fichiers avec un espace à l'intérieur du nom?

0 votes

Avez-vous essayé d'ajouter les fichiers à l'ancienne ? Comme FILES[0] = ...? (Edit: Je viens juste de le faire ; ne fonctionne pas. Intéressant).

0 votes

Toutes les réponses ici ne fonctionnent pas pour moi en utilisant Cygwin. Il fait des choses bizarres s'il y a des espaces dans les noms de fichiers, point. Je contourne cela en créant un "tableau" dans un fichier texte listant tous les éléments avec lesquels je veux travailler, et en itérant sur les lignes du fichier: Le formatage perturbe les backticks prévus ici entourant la commande entre parenthèses: IFS=""; array=(find . -maxdepth 1 -type f -iname \*.$1 -printf '%f\n'); for element in ${array[@]}; do echo $element; done

135voto

Dan Fego Points 6658

Je pense que le problème pourrait être en partie lié à la façon dont vous accédez aux éléments. Si je fais simplement for elem in $FILES, je rencontre le même problème que vous. Cependant, si j'accède au tableau via ses indices, de cette manière, cela fonctionne si j'ajoute les éléments de façon numérique ou avec des échappements :

for ((i = 0; i < ${#FILES[@]}; i++))
do
    echo "${FILES[$i]}"
done

N'importe laquelle de ces déclarations de $FILES devrait fonctionner :

FILES=(2011-09-04\ 21.43.02.jpg
2011-09-05\ 10.23.14.jpg
2011-09-09\ 12.31.16.jpg
2011-09-11\ 08.43.12.jpg)

ou

FILES=("2011-09-04 21.43.02.jpg"
"2011-09-05 10.23.14.jpg"
"2011-09-09 12.31.16.jpg"
"2011-09-11 08.43.12.jpg")

ou

FILES[0]="2011-09-04 21.43.02.jpg"
FILES[1]="2011-09-05 10.23.14.jpg"
FILES[2]="2011-09-09 12.31.16.jpg"
FILES[3]="2011-09-11 08.43.12.jpg"

8 votes

Notez que vous devez utiliser des guillemets lorsque vous utilisez les éléments du tableau (par exemple, echo "${FILES[$i]}"). Peu importe pour echo, mais ça compte pour tout ce qui l'utilise comme nom de fichier.

30 votes

Il n'est pas nécessaire de boucler sur les index lorsque vous pouvez boucler sur les éléments avec for f in "${FILES[@]}".

11 votes

@MarkEdgar j'ai des problèmes avec for f in ${FILES[@]} lorsque les membres du tableau contiennent des espaces. Il semble que le tableau entier soit réinterprété à nouveau, avec les espaces divisant vos membres existants en deux éléments ou plus. Il semble que les " " soient très importants

102voto

Pumbaa80 Points 27066

Il doit y avoir quelque chose qui ne va pas avec la façon dont vous accédez aux éléments du tableau. Voici comment c'est fait :

for elem in "${files[@]}"
...

D'après la page de manuel de bash :

N'importe quel élément d'un tableau peut être référencé en utilisant ${name[subscript]}. ... Si le subscript est @ ou *, le mot se développe en tous les membres de name. Ces subscripts diffèrent uniquement lorsque le mot apparaît entre guillemets doubles. Si le mot est entre guillemets doubles, ${name[*]} se développe en un seul mot avec la valeur de chaque membre du tableau séparés par le premier caractère de la variable spéciale IFS, et ${name[@]} développe chaque élément de name en un mot séparé.

Évidemment, vous devriez également utiliser des guillemets doubles lorsque vous accédez à un seul élément

cp "${files[0]}" /tmp

3 votes

La solution la plus propre et la plus élégante de ce lot, même si je devrais réitérer que chaque élément défini dans le tableau doit être mis entre guillemets.

0 votes

Bien que la réponse de Dan Fego soit efficace, c'est la façon la plus idiomatique de gérer les espaces dans les éléments.

3 votes

En venant d'autres langages de programmation, la terminologie de cet extrait est vraiment difficile à comprendre. De plus, la syntaxe est déconcertante. Je vous serais extrêmement reconnaissant si vous pouviez aller un peu plus loin à ce sujet? En particulier se développe en un seul mot avec la valeur de chaque membre du tableau séparé par le premier caractère de la variable spéciale IFS

48voto

Khushneet Points 121

Vous devez utiliser IFS pour arrêter l'espace comme délimiteur d'élément.

FILES=("2011-09-04 21.43.02.jpg"
       "2011-09-05 10.23.14.jpg"
       "2011-09-09 12.31.16.jpg"
       "2011-09-11 08.43.12.jpg")
IFS=""
for jpg in ${FILES[*]}
do
    echo "${jpg}"
done

Si vous voulez séparer sur la base de . alors il suffit de faire IFS="." Espérons que cela vous aide :)

3 votes

J'ai dû déplacer le IFS="" avant l'assignation du tableau mais c'est la réponse correcte.

0 votes

Je suis en train d'utiliser plusieurs tableaux pour analyser des informations et je vais avoir l'effet de IFS="" fonctionnant dans un seul d'entre eux. Une fois que j'utilise IFS="", tous les autres tableaux cessent d'analyser correctement. Des indices à ce sujet?

0 votes

Paulo, voir une autre réponse ici qui pourrait être meilleure pour votre cas : stackoverflow.com/a/9089186/1041319. N'ai pas essayé IFS="", et il semble que cela résolve élégamment le problème - mais votre exemple montre pourquoi on peut rencontrer des problèmes dans certains cas. Il est peut-être possible de définir IFS="" sur une seule ligne, mais cela pourrait être encore plus déroutant que l'autre solution.

14voto

Dean Hall Points 111

Je suis d'accord avec les autres qu'il est probable que le problème réside dans la façon dont vous accédez aux éléments. Citer les noms de fichiers dans l'assignation de tableau est correct :

FILES=(
  "2011-09-04 21.43.02.jpg"
  "2011-09-05 10.23.14.jpg"
  "2011-09-09 12.31.16.jpg"
  "2011-09-11 08.43.12.jpg"
)

for f in "${FILES[@]}"
do
  echo "$f"
done

Utiliser des guillemets doubles autour de tout tableau de la forme "${FILES[@]}" divise le tableau en un mot par élément du tableau. Cela ne fait pas de division de mots au-delà de cela.

Utiliser "${FILES[*]}" a également une signification spéciale, mais cela assemble les éléments du tableau avec le premier caractère de $IFS, ce qui donne un mot, ce qui n'est probablement pas ce que vous voulez.

Utiliser un simple ${array[@]} ou ${array[*]} soumet le résultat de cette expansion à une division de mots supplémentaire, donc vous obtiendrez des mots divisés par des espaces (et tout autre caractère dans $IFS) au lieu d'un mot par élément du tableau.

Utiliser une boucle for de style C est également correct et évite de se soucier de la division de mots si vous n'êtes pas clair à ce sujet:

for (( i = 0; i < ${#FILES[@]}; i++ ))
do
  echo "${FILES[$i]}"
done

2voto

iiSeymour Points 34967

L'échappement fonctionne.

#!/bin/bash

FILES=(2011-09-04\ 21.43.02.jpg
2011-09-05\ 10.23.14.jpg
2011-09-09\ 12.31.16.jpg
2011-09-11\ 08.43.12.jpg)

echo ${FILES[0]}
echo ${FILES[1]}
echo ${FILES[2]}
echo ${FILES[3]}

Sortie:

$ ./test.sh
2011-09-04 21.43.02.jpg
2011-09-05 10.23.14.jpg
2011-09-09 12.31.16.jpg
2011-09-11 08.43.12.jpg

En mettant les chaînes entre guillemets, on obtient également la même sortie.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X