Чтение разделенной строки в массив в Bash

207

У меня есть переменная, которая содержит разделенную пробелами строку:

line="1 1.50 string"

Я хочу разделить эту строку пробелом в качестве разделителя и сохранить результат в массиве, так что следующее:

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

выходы

1
1.50
string

Где-то я нашел решение, которое не работает:

arr=$(echo ${line})

Если после этого я выполню приведенные выше операторы echo, я получу:

1 1.50 string
[empty line]
[empty line]

Я тоже пробовал

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

с тем же результатом. Может кто-нибудь помочь, пожалуйста?

Никола Новак
источник
Смотрите этот ответ на Unix & Linux Stack Exchange: Использование sed с herestring (<<<) и прочитайте -a . set -f; arr=($string); set +fкажется быстрее чем read -r -a <<< $string.
Codeforester

Ответы:

332

Чтобы преобразовать строку в массив, используйте

arr=($line)

или

read -a arr <<< $line

Крайне важно не использовать кавычки, так как это делает свое дело.

кэв
источник
7
и провести проверку for i in ${arr[@]}; do echo $i; done
работоспособности
5
или простоecho ${arr[@]}
Banjer
13
Оба способа могут потерпеть неудачу, если в нем $lineесть символы-заглушки. mkdir x && cd x && touch A B C && line="*" arr=($line); echo ${#arr[@]}дает 3
Тино
3
declare -a "arr=($line)"будет игнорировать IFSразделители внутри указанных строк
Дейв
4
@ Тино Нет. Когда line='*', read -a arr <<<$lineвсегда работает, но только arr=($line)терпит неудачу.
Джонни Вонг
39

Попробуй это:

arr=(`echo ${line}`);
похотливый
источник
5
Приятно - Это решение также работает в оболочке Z, где некоторые из вышеперечисленных подходов не работают.
Кит Хьюджитт
Это работает, не могли бы вы объяснить, почему это работает?
SmartWjW
2
Замечание: это также не работает, когда в строке есть «*», например,line='*'
Джонни Вонг
34

В: arr=( $line ). «Раскол» связан с «шаром».
Подстановочные знаки ( *, ?и []) будут расширены до соответствующих имен файлов.

Правильное решение немного сложнее:

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

Нет проблем с глобализацией; разделенный символ установлен $IFS, переменные указаны.

Исаак
источник
5
Это должен быть принятый ответ. Утверждение arr=($line)в принятом ответе страдает от глобальных проблем. Например, попробуйте: line="twinkling *"; arr=($line); declare -p arr.
Codeforester
Цитирование необязательно для этой строки, <<<но может быть хорошей идеей использовать двойные кавычки для согласованности и читабельности.
Codeforester
7

Если вам нужно расширение параметров, попробуйте:

eval "arr=($line)"

Например, возьмите следующий код.

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

Если текущий каталог содержит файлы a.txt, b.txtи c.txt, то выполнение кода приведет к следующему выводу.

a
b
c d
*
a.txt
b.txt
c.txt
Дэвид Андерсон
источник
-9
line="1 1.50 string"

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

for x in $arr
do
echo "> [$x]"
done
Hitesh
источник
Цикл неправильный, он разбивает массив на мелкие, а труба на trних лишняя, но "${arr[@]}"вместо этого она должна зацикливаться, а не$arr
Zorf