Lecture d'une chaîne délimitée dans un tableau dans Bash

207

J'ai une variable qui contient une chaîne délimitée par des espaces:

line="1 1.50 string"

Je veux diviser cette chaîne avec de l'espace comme délimiteur et stocker le résultat dans un tableau, de sorte que les éléments suivants:

echo ${arr[0]}
echo ${arr[1]}
echo ${arr[2]}

les sorties

1
1.50
string

Quelque part, j'ai trouvé une solution qui ne fonctionne pas:

arr=$(echo ${line})

Si j'exécute les instructions echo ci-dessus après cela, j'obtiens:

1 1.50 string
[empty line]
[empty line]

J'ai aussi essayé

IFS=" "
arr=$(echo ${line})

avec le même résultat. Quelqu'un peut-il m'aider, s'il vous plaît?

Nikola Novak
la source
Voir cette réponse sur Unix et Linux Stack Exchange: Utilisation de sed avec herestring (<<<) et lisez -a . set -f; arr=($string); set +fsemble être plus rapide que read -r -a <<< $string.
codeforester

Réponses:

332

Afin de convertir une chaîne en tableau, veuillez utiliser

arr=($line)

ou

read -a arr <<< $line

Il est crucial de ne pas utiliser de guillemets car cela fait l'affaire.

kev
la source
7
et pour faire un bilan de santé de votre magnifique nouveau tableau:for i in ${arr[@]}; do echo $i; done
Banjer
5
ou tout simplementecho ${arr[@]}
Banjer
13
Les deux façons peuvent échouer si elles $linecontiennent des caractères globbing. mkdir x && cd x && touch A B C && line="*" arr=($line); echo ${#arr[@]}donne 3
Tino
3
declare -a "arr=($line)"ignorera les IFSdélimiteurs à l'intérieur des chaînes citées
Dave
4
@Tino Non. Quand line='*', read -a arr <<<$linetoujours travailler, mais arr=($line)échoue seulement .
Johnny Wong
39

Essaye ça:

arr=(`echo ${line}`);
Excité
la source
5
Nice - Cette solution fonctionne également dans le shell Z où certaines des autres approches ci-dessus échouent.
Keith Hughitt
Son fait le travail, pourriez-vous s'il vous plaît expliquer pourquoi cela fonctionne?
smartwjw
2
Remarque: cela ne fonctionne pas non plus lorsque la ligne contient '*', commeline='*'
Johnny Wong
34

Dans: arr=( $line ). Le "split" est associé à "glob".
Les caractères génériques ( *, ?et []) seront étendus aux noms de fichiers correspondants.

La bonne solution n'est que légèrement plus complexe:

IFS=' ' read -a arr <<< "$line"

Aucun problème de globbing; le caractère fractionné est défini dans $IFS, variables citées.

Isaac
la source
5
Cela devrait être la réponse acceptée. L'énoncé arr=($line)de la réponse acceptée souffre de problèmes de globalisation. Par exemple, essayez: line="twinkling *"; arr=($line); declare -p arr.
codeforester
La citation est facultative pour l'héritage, <<<mais il peut être judicieux de toujours utiliser des guillemets doubles pour la cohérence et la lisibilité.
codeforester
7

Si vous avez besoin d'une extension des paramètres, essayez:

eval "arr=($line)"

Par exemple, prenez le code suivant.

line='a b "c d" "*" *'
eval "arr=($line)"
for s in "${arr[@]}"; do 
    echo "$s"
done

Si le répertoire actuel contenait les fichiers a.txt, b.txtet c.txt, alors l'exécution du code produirait la sortie suivante.

a
b
c d
*
a.txt
b.txt
c.txt
David Anderson
la source
-9
line="1 1.50 string"

arr=$( $line | tr " " "\n")

for x in $arr
do
echo "> [$x]"
done
Hitesh
la source
Le bouclage est incorrect, il divise bien le tableau et le tuyau trest superflu mais il devrait boucler à la "${arr[@]}"place, pas$arr
Zorf