Membaca string yang dibatasi menjadi array di Bash

207

Saya memiliki variabel yang berisi string yang dibatasi ruang:

line="1 1.50 string"

Saya ingin membagi string itu dengan spasi sebagai pembatas dan menyimpan hasilnya dalam array, sehingga berikut ini:

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

output

1
1.50
string

Di suatu tempat saya menemukan solusi yang tidak berhasil:

arr=$(echo ${line})

Jika saya menjalankan pernyataan gema di atas setelah ini, saya mendapatkan:

1 1.50 string
[empty line]
[empty line]

Saya juga mencoba

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

dengan hasil yang sama. Bisakah seseorang membantu?

Nikola Novak
sumber
Lihat jawaban ini di Unix & Linux Stack Exchange: Menggunakan sed dengan herestring (<<<) dan baca -a . set -f; arr=($string); set +ftampaknya lebih cepat daripada read -r -a <<< $string.
codeforester

Jawaban:

332

Untuk mengkonversi string menjadi array, silakan gunakan

arr=($line)

atau

read -a arr <<< $line

Sangat penting untuk tidak menggunakan tanda kutip karena ini berhasil.

kev
sumber
7
dan untuk melakukan pemeriksaan kewarasan array baru Anda yang indah:for i in ${arr[@]}; do echo $i; done
Banjer
5
atau hanyaecho ${arr[@]}
Banjer
13
Kedua cara mungkin gagal jika $linememiliki karakter globbing di dalamnya. mkdir x && cd x && touch A B C && line="*" arr=($line); echo ${#arr[@]}memberi 3
Tino
3
declare -a "arr=($line)"akan mengabaikan IFSpembatas di dalam string yang dikutip
Dave
4
@Tino No. Kapan line='*', read -a arr <<<$lineselalu bekerja, tetapi hanya arr=($line)gagal.
Johnny Wong
39

Coba ini:

arr=(`echo ${line}`);
Randy
sumber
5
Bagus - Solusi ini juga berfungsi di Z shell di mana beberapa pendekatan lain di atas gagal.
Keith Hughitt
Ini berhasil, bisakah Anda menjelaskan mengapa itu berhasil?
smartwjw
2
Catatan: ini tidak berfungsi baik ketika baris memiliki '*' di dalamnya, sepertiline='*'
Johnny Wong
34

Dalam: arr=( $line ). "Split" dikaitkan dengan "glob".
Wildcard ( *, ?dan []) akan diperluas ke nama file yang cocok.

Solusi yang benar hanya sedikit lebih kompleks:

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

Tidak ada masalah menggumpal; karakter split diatur $IFS, variabel yang dikutip.

Ishak
sumber
5
Ini harus menjadi jawaban yang diterima. Pernyataan arr=($line)dalam jawaban yang diterima menderita masalah globbing. Sebagai contoh, cobalah: line="twinkling *"; arr=($line); declare -p arr.
codeforester
Mengutip adalah opsional untuk herestring, <<<tetapi mungkin ide yang baik untuk tetap menggunakan tanda kutip ganda untuk konsistensi dan keterbacaan.
codeforester
7

Jika Anda membutuhkan ekspansi parameter, cobalah:

eval "arr=($line)"

Misalnya, ambil kode berikut.

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

Jika direktori saat ini berisi file a.txt, b.txtdan c.txt, mengeksekusi kode akan menghasilkan output berikut.

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

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

for x in $arr
do
echo "> [$x]"
done
Hitesh
sumber
Perulangan salah, ia membagi array dengan baik dan pipa menjadi trberlebihan tetapi harus melewatinya "${arr[@]}", bukan$arr
Zorf