Odczytywanie ograniczonego ciągu znaków do tablicy w Bash

206

Mam zmienną, która zawiera ciąg rozdzielany spacjami:

line="1 1.50 string"

Chcę podzielić ten ciąg ze spacją jako separator i zapisać wynik w tablicy, aby:

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

wyjścia

1
1.50
string

Gdzieś znalazłem rozwiązanie, które nie działa:

arr=$(echo ${line})

Jeśli po tym uruchomię powyższe instrukcje echa, otrzymam:

1 1.50 string
[empty line]
[empty line]

Też próbowałem

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

z tym samym wynikiem. Czy ktoś może pomóc?

Nikola Novak
źródło
Zobacz tę odpowiedź na temat wymiany stosów w systemach Unix i Linux: Używanie sed z Herestring (<<<) i czytanie -a . set -f; arr=($string); set +fwydaje się być szybszy niż read -r -a <<< $string.
codeforester

Odpowiedzi:

331

Aby przekonwertować ciąg znaków na tablicę, użyj

arr=($line)

lub

read -a arr <<< $line

Ważne jest, aby nie używać cudzysłowów, ponieważ to załatwia sprawę.

kev
źródło
7
i zrobić kontrolę czystości swojego pięknego nowego zestawu:for i in ${arr[@]}; do echo $i; done
Banjer
5
lub po prostuecho ${arr[@]}
Banjer
13
Oba sposoby mogą się nie powieść, jeśli $linezawierają znaki globowania. mkdir x && cd x && touch A B C && line="*" arr=($line); echo ${#arr[@]}daje 3
Tino
3
declare -a "arr=($line)"zignoruje IFSograniczniki wewnątrz cytowanych ciągów
Dave
4
@Tino nr Kiedy line='*', read -a arr <<<$linezawsze działa, ale tylko arr=($line)się nie powiedzie.
Johnny Wong,
39

Spróbuj tego:

arr=(`echo ${line}`);
Niespokojny
źródło
5
Fajnie - to rozwiązanie działa również w powłoce Z, gdzie niektóre inne powyższe podejścia zawodzą.
Keith Hughitt
To działa, czy mógłbyś wyjaśnić, dlaczego to działa?
smartwjw,
2
Uwaga: to również nie działa, gdy w wierszu znajduje się „*”, na przykładline='*'
Johnny Wong,
34

W: arr=( $line ). „Podział” kojarzy się z „glob”.
Symbole wieloznaczne ( *, ?i []) zostaną rozwinięte do pasujących nazw plików.

Prawidłowe rozwiązanie jest tylko nieco bardziej złożone:

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

Nie ma problemu z globowaniem; znak podziału jest ustawiony $IFS, cytowane zmienne.

Izaak
źródło
5
To powinna być zaakceptowana odpowiedź. Stwierdzenie arr=($line)w przyjętej odpowiedzi cierpi z powodu problemów globowania. Na przykład, spróbuj: line="twinkling *"; arr=($line); declare -p arr.
codeforester
Cytowanie jest opcjonalne w przypadku znaków tutaj, <<<ale dobrym pomysłem może być nadal stosowanie podwójnych cudzysłowów dla zachowania spójności i czytelności.
codeforester
6

Jeśli potrzebujesz rozszerzenia parametrów, spróbuj:

eval "arr=($line)"

Na przykład weź następujący kod.

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

Jeśli bieżący katalog zawiera pliki a.txt, b.txta c.txtnastępnie wykonanie kodu wygeneruje następujące dane wyjściowe.

a
b
c d
*
a.txt
b.txt
c.txt
David Anderson
źródło
-9
line="1 1.50 string"

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

for x in $arr
do
echo "> [$x]"
done
Hitesh
źródło
trPętla jest nieprawidłowa, dobrze dzieli tablicę, a rura jest zbędna, ale "${arr[@]}"zamiast tego powinna się zapętlić , nie$arr
Zorf