Pertanyaan Bagaimana cara membagi string pada pemisah di Bash?


Saya memiliki string ini yang disimpan dalam variabel:

IN="bla@some.com;john@home.com"

Sekarang saya ingin membagi string oleh ; pemisah sehingga saya punya:

ADDR1="bla@some.com"
ADDR2="john@home.com"

Saya tidak perlu itu ADDR1 dan ADDR2 variabel. Jika mereka adalah elemen array yang lebih baik.


Setelah saran dari jawaban di bawah, saya mendapatkan yang berikut ini yang saya cari:

#!/usr/bin/env bash

IN="bla@some.com;john@home.com"

mails=$(echo $IN | tr ";" "\n")

for addr in $mails
do
    echo "> [$addr]"
done

Keluaran:

> [bla@some.com]
> [john@home.com]

Ada solusi yang melibatkan pengaturan Internal_field_separator (IFS) ke ;. Saya tidak yakin apa yang terjadi dengan jawaban itu, bagaimana Anda mengatur ulang IFS kembali ke default?

KEMBALI: IFS solusi, saya mencoba ini dan itu berhasil, saya menyimpan yang lama IFS dan kemudian pulihkan:

IN="bla@some.com;john@home.com"

OIFS=$IFS
IFS=';'
mails2=$IN
for x in $mails2
do
    echo "> [$x]"
done

IFS=$OIFS

BTW, ketika saya mencoba

mails2=($IN)

Saya hanya mendapat string pertama saat mencetaknya dalam lingkaran, tanpa tanda kurung $IN berhasil.


1507
2018-05-28 02:03


asal


Jawaban:


Anda dapat mengatur pemisah bidang internal (IFS) variabel, dan kemudian biarkan mem-parsing ke dalam array. Ketika ini terjadi dalam suatu perintah, maka tugas untuk IFS hanya terjadi pada lingkungan perintah tunggal (untuk read ). Ini kemudian mem-parsing input sesuai dengan IFS nilai variabel ke dalam sebuah array, yang kemudian dapat diulang.

IFS=';' read -ra ADDR <<< "$IN"
for i in "${ADDR[@]}"; do
    # process "$i"
done

Ini akan mem-parse satu baris item yang dipisahkan oleh ;, mendorongnya ke dalam array. Barang untuk diproses seluruhnya $IN, setiap kali satu baris input dipisahkan oleh ;:

 while IFS=';' read -ra ADDR; do
      for i in "${ADDR[@]}"; do
          # process "$i"
      done
 done <<< "$IN"

922
2018-05-28 02:23



Diambil dari Bash shell script split array:

IN="bla@some.com;john@home.com"
arrIN=(${IN//;/ })

Penjelasan:

Konstruksi ini menggantikan semua kejadian ';' (inisial // berarti ganti global) dalam string IN dengan ' ' (spasi tunggal), kemudian menafsirkan string spasi-dipisahkan sebagai larik (itulah yang dilakukan tanda kurung di sekitarnya).

Sintaks yang digunakan di dalam kurung kurawal untuk menggantikan masing-masing ';' karakter dengan ' ' karakter disebut Ekspansi Parameter.

Ada beberapa gotchas umum:

  1. Jika string asli memiliki spasi, Anda harus menggunakannya IFS:
    • IFS=':'; arrIN=($IN); unset IFS;
  2. Jika string asli memiliki spasi dan pemisah adalah garis baru, Anda dapat mengatur IFS dengan:
    • IFS=$'\n'; arrIN=($IN); unset IFS;

743
2018-03-10 09:00



Jika Anda tidak keberatan memprosesnya dengan segera, saya ingin melakukan ini:

for i in $(echo $IN | tr ";" "\n")
do
  # process
done

Anda bisa menggunakan loop semacam ini untuk menginisialisasi array, tetapi mungkin ada cara yang lebih mudah untuk melakukannya. Semoga ini bisa membantu.


207
2018-05-28 02:09



Jawaban yang kompatibel

Untuk pertanyaan SO ini, sudah ada banyak cara berbeda untuk melakukan ini . Tapi bash punya banyak khusus fitur, disebut demikian bashisme itu bekerja dengan baik, tetapi itu tidak akan berhasil di lainnya .

Khususnya, array, array asosiatif, dan substitusi pola murni bashisms dan mungkin tidak berfungsi di bawah yang lain kerang.

Pada saya Debian GNU / Linux, ada sebuah standar shell disebut , tapi saya tahu banyak orang yang suka menggunakannya .

Akhirnya, dalam situasi yang sangat kecil, ada alat khusus yang disebut  dengan interpreter cangkangnya sendiri ().

String yang diminta

Contoh string dalam pertanyaan SO adalah:

IN="bla@some.com;john@home.com"

Karena ini bisa berguna dengan ruang putih dan sebagai ruang putih dapat memodifikasi hasil rutin, saya lebih suka menggunakan string contoh ini:

 IN="bla@some.com;john@home.com;Full Name <fulnam@other.org>"

Pisahkan string berdasarkan pembatas dalam  (versi> = 4.2)

Dibawah murni bash, kami dapat menggunakan array dan IFS:

var="bla@some.com;john@home.com;Full Name <fulnam@other.org>"

oIFS="$IFS"
IFS=";"
declare -a fields=($var)
IFS="$oIFS"
unset oIFS


121
2018-04-13 14:20



Bagaimana dengan pendekatan ini:

IN="bla@some.com;john@home.com" 
set -- "$IN" 
IFS=";"; declare -a Array=($*) 
echo "${Array[@]}" 
echo "${Array[0]}" 
echo "${Array[1]}" 

Sumber


80
2018-05-28 10:31



Saya telah melihat beberapa jawaban yang merujuk pada cut perintah, tetapi mereka semua telah dihapus. Agak aneh bahwa tidak ada yang menguraikan itu, karena saya pikir itu salah satu dari perintah yang lebih berguna untuk melakukan hal semacam ini, terutama untuk mengurai file log yang dibatasi.

Dalam kasus pemisahan contoh spesifik ini ke dalam susunan skrip bash, tr mungkin lebih efisien, tetapi cut dapat digunakan, dan lebih efektif jika Anda ingin menarik bidang tertentu dari tengah.

Contoh:

$ echo "bla@some.com;john@home.com" | cut -d ";" -f 1
bla@some.com
$ echo "bla@some.com;john@home.com" | cut -d ";" -f 2
john@home.com

Anda dapat dengan jelas memasukkannya ke dalam satu lingkaran, dan mengiterasi parameter -f untuk menarik setiap bidang secara independen.

Ini menjadi lebih berguna ketika Anda memiliki file log terbatas dengan baris seperti ini:

2015-04-27|12345|some action|an attribute|meta data

cut sangat berguna untuk bisa cat file ini dan pilih bidang tertentu untuk diproses lebih lanjut.


74
2018-04-27 18:20



Ini berhasil untuk saya:

string="1;2"
echo $string | cut -d';' -f1 # output is 1
echo $string | cut -d';' -f2 # output is 2

65
2017-08-11 20:45



echo "bla@some.com;john@home.com" | sed -e 's/;/\n/g'
bla@some.com
john@home.com

59
2018-05-28 02:12



Ini juga berfungsi:

IN="bla@some.com;john@home.com"
echo ADD1=`echo $IN | cut -d \; -f 1`
echo ADD2=`echo $IN | cut -d \; -f 2`

Hati-hati, solusi ini tidak selalu benar. Jika Anda melewatkan "bla@some.com" saja, itu akan menugaskannya ke ADD1 dan ADD2.


57
2017-09-08 05:01