Saya memiliki string ini disimpan dalam variabel:
IN="[email protected];[email protected]"
Sekarang saya ingin membagi string dengan ;
pembatas sehingga saya memiliki:
ADDR1="[email protected]"
ADDR2="[email protected]"
Saya tidak perlu ADDR1
dan ADDR2
variabel. Jika mereka adalah elemen dari array itu bahkan lebih baik.
Setelah saran dari jawaban di bawah ini, saya berakhir dengan yang berikut yang saya cari:
#!/usr/bin/env bash
IN="[email protected];[email protected]"
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?
RE: IFS
solusi, saya mencoba ini dan berhasil, saya menyimpan yang lama IFS
dan mengembalikannya:
IN="[email protected];[email protected]"
OIFS=$IFS
IFS=';'
mails2=$IN
for x in $mails2
do
echo "> [$x]"
done
IFS=$OIFS
BTW, ketika saya mencoba
mails2=($IN)
Saya hanya mendapatkan string pertama saat mencetaknya dalam lingkaran, tanpa tanda kurung di $IN
sekitarnya berfungsi.
local IFS=...
jika memungkinkan; (b) -1 untukunset IFS
, ini tidak persis me-reset IFS ke nilai default, meskipun saya percaya IFS yang tidak disetel berperilaku sama dengan nilai default IFS ($ '\ t \ n'), namun tampaknya praktik yang buruk untuk mengasumsikan secara membabi buta bahwa kode Anda tidak akan pernah dipanggil dengan IFS diatur ke nilai khusus; (c) ide lain adalah meminta subshell:(IFS=$custom; ...)
ketika subshell keluar IFS akan kembali ke apa pun awalnya.ruby -e "puts ENV.fetch('PATH').split(':')"
. Jika Anda ingin tetap bash murni tidak akan membantu tetapi menggunakan bahasa skrip apa pun yang memiliki pemisahan bawaan lebih mudah.for x in $(IFS=';';echo $IN); do echo "> [$x]"; done
\n
hanya spasi. Jadi baris terakhir adalahmails=($(echo $IN | tr ";" " "))
. Jadi sekarang saya dapat memeriksa elemen-elemenmails
dengan menggunakan notasi arraymails[index]
atau hanya mengulangi dalam satu lingkaranJawaban:
Anda bisa mengatur variabel pemisah bidang internal (IFS), dan kemudian membiarkannya menguraikan menjadi array. Ketika ini terjadi dalam suatu perintah, maka penugasan untuk
IFS
hanya terjadi pada lingkungan perintah tunggal (untukread
). Ini kemudian mem-parsing input sesuai dengan nilaiIFS
variabel ke dalam array, yang kemudian dapat kita iterate.Ini akan mengurai satu baris item yang dipisahkan oleh
;
, mendorongnya ke dalam array. Hal-hal untuk memproses keseluruhan$IN
, setiap kali satu baris input dipisahkan oleh;
:sumber
IFS
pada baris yang samaread
dengan tanpa titik koma atau pemisah lainnya, sebagai lawan dalam perintah terpisah, lingkup ke perintah itu - sehingga selalu "dikembalikan"; Anda tidak perlu melakukan apa pun secara manual.$IN
dikutip. Bug diperbaiki padabash
4.3.Diambil dari array pemisah skrip Bash shell :
Penjelasan:
Konstruksi ini menggantikan semua kemunculan
';'
(inisialisasi//
global berarti) dalam stringIN
dengan' '
(spasi tunggal), kemudian mengartikan string yang dibatasi ruang sebagai array (itulah yang dilakukan kurung di sekitarnya).Sintaks yang digunakan di dalam kurung kurawal untuk mengganti setiap
';'
karakter dengan' '
karakter disebut Parameter Expansion .Ada beberapa gotcha yang umum:
IFS=':'; arrIN=($IN); unset IFS;
IFS=$'\n'; arrIN=($IN); unset IFS;
sumber
IN="[email protected];[email protected];*;broken apart"
. Singkatnya: pendekatan ini akan pecah, jika token Anda berisi ruang dan / atau karakter yang disematkan. seperti*
itu terjadi untuk membuat nama file token yang cocok di folder saat ini.;*;
, maka*
akan diperluas ke daftar nama file di direktori saat ini. -1Jika Anda tidak keberatan memprosesnya segera, saya suka melakukan ini:
Anda bisa menggunakan loop semacam ini untuk menginisialisasi array, tetapi mungkin ada cara yang lebih mudah untuk melakukannya. Semoga ini bisa membantu.
sumber
IN="[email protected];[email protected];*;broken apart"
. Singkatnya: pendekatan ini akan pecah, jika token Anda berisi ruang dan / atau karakter yang disematkan. seperti*
itu terjadi untuk membuat nama file token yang cocok di folder saat ini.Jawaban yang kompatibel
Ada banyak cara berbeda untuk melakukan ini pesta.
Namun, penting untuk diketahui terlebih dahulu yang
bash
memiliki banyak fitur khusus (disebut bashism ) yang tidak akan berfungsi di fitur lainnyakulit.Secara khusus, array , array asosiatif , dan substitusi pola , yang digunakan dalam solusi dalam posting ini serta yang lain di utas, adalah bashism dan mungkin tidak berfungsi di bawah cangkang lain yang banyak digunakan orang.
Sebagai contoh: pada Debian GNU / Linux saya , ada shell standar yang disebutberlari; Saya tahu banyak orang yang suka menggunakan shell lain yang disebutksh; dan ada juga alat khusus yang disebutbusybox dengan penerjemah shellnya sendiri (Abu).
String yang diminta
String yang akan dibagi dalam pertanyaan di atas adalah:
Saya akan menggunakan versi modifikasi dari string ini untuk memastikan bahwa solusi saya kuat untuk string yang berisi spasi putih, yang dapat memecahkan solusi lain:
Split string berdasarkan pembatas di pesta (versi> = 4.2)
Secara murni
bash
, kita bisa membuat array dengan elemen yang dipisahkan oleh nilai sementara untuk IFS ( pemisah bidang input ). IFS, antara lain, memberi tahubash
karakter mana yang harus diperlakukan sebagai pembatas antara elemen ketika mendefinisikan array:Dalam versi yang lebih baru dari
bash
, awalan perintah dengan definisi IFS mengubah IFS untuk perintah yang hanya dan me-reset ke nilai sebelumnya segera setelah itu. Ini berarti kita dapat melakukan hal di atas hanya dalam satu baris:Kita dapat melihat bahwa string
IN
telah disimpan ke dalam array bernamafields
, dipisah pada titik koma:(Kami juga dapat menampilkan konten dari variabel-variabel ini menggunakan
declare -p
:)Perhatikan bahwa
read
ini adalah cara tercepat untuk melakukan pemecahan karena tidak ada garpu atau sumber daya eksternal yang disebut.Setelah array didefinisikan, Anda dapat menggunakan loop sederhana untuk memproses setiap bidang (atau, lebih tepatnya, setiap elemen dalam array yang sekarang telah Anda tetapkan):
Atau Anda bisa menjatuhkan setiap bidang dari array setelah diproses menggunakan pendekatan pergeseran , yang saya suka:
Dan jika Anda hanya menginginkan cetakan array yang sederhana, Anda bahkan tidak perlu mengulanginya:
Perbarui: baru-baru ini pesta > = 4.4
Di versi yang lebih baru
bash
, Anda juga dapat bermain dengan perintahmapfile
:Sintaks ini mempertahankan karakter khusus, baris baru, dan bidang kosong!
Jika Anda tidak ingin memasukkan bidang kosong, Anda dapat melakukan hal berikut:
Dengan
mapfile
, Anda juga dapat melewati mendeklarasikan array dan secara implisit "loop" di atas elemen yang dibatasi, memanggil fungsi pada masing-masing:(Catatan:
\0
di akhir string format tidak berguna jika Anda tidak peduli dengan bidang kosong di akhir string atau mereka tidak ada.)Atau Anda bisa menggunakan
<<<
, dan di badan fungsi menyertakan beberapa pemrosesan untuk menghapus baris baru yang ditambahkan:Split string berdasarkan pembatas di kulit
Jika Anda tidak dapat menggunakan
bash
, atau jika Anda ingin menulis sesuatu yang dapat digunakan di banyak shell yang berbeda, Anda sering tidak dapat menggunakan bashism - dan ini termasuk array yang telah kami gunakan dalam solusi di atas.Namun, kita tidak perlu menggunakan array untuk mengulang "elemen" string. Ada sintaks yang digunakan dalam banyak shell untuk menghapus substring dari string dari kemunculan pertama atau terakhir suatu pola. Perhatikan bahwa
*
wildcard yang mewakili nol atau lebih karakter:(Kurangnya pendekatan ini dalam solusi yang diposting sejauh ini adalah alasan utama saya menulis jawaban ini;)
Seperti yang dijelaskan oleh Score_Under :
Menggunakan sintaks di atas, kita dapat membuat pendekatan di mana kita mengekstraksi "elemen" substring dari string dengan menghapus substring hingga atau setelah pembatas.
Kode kunci di bawah berfungsi dengan baik di pesta(termasuk Mac OS
bash
),berlari, ksh, dan busyboxini Abu:Selamat bersenang-senang!
sumber
#
,##
,%
, dan%%
substitusi memiliki apa yang IMO penjelasan lebih mudah untuk mengingat (untuk berapa banyak mereka menghapus):#
dan%
menghapus kemungkinan pencocokan string terpendek, dan##
dan%%
menghapus terpanjang mungkin.IFS=\; read -a fields <<<"$var"
gagal pada baris dan menambahkan baris baru trailing. Solusi lain menghilangkan bidang kosong yang tertinggal.for sep in "#" "ł" "@" ; do ... var="${var#*$sep}" ...
Saya telah melihat beberapa jawaban merujuk
cut
perintah, tetapi semuanya sudah dihapus. Agak aneh bahwa tidak ada yang menjelaskan hal itu, karena saya pikir itu adalah salah satu perintah yang lebih berguna untuk melakukan hal semacam ini, terutama untuk mem-parsing file log yang dibatasi.Dalam hal memecah contoh spesifik ini menjadi array skrip bash,
tr
mungkin lebih efisien, tetapicut
dapat digunakan, dan lebih efektif jika Anda ingin menarik bidang tertentu dari tengah.Contoh:
Anda jelas dapat memasukkannya ke dalam satu lingkaran, dan lakukan iterasi pada parameter -f untuk menarik setiap bidang secara independen.
Ini menjadi lebih berguna ketika Anda memiliki file log yang dibatasi dengan baris seperti ini:
cut
sangat berguna untuk dapatcat
file ini dan memilih bidang tertentu untuk diproses lebih lanjut.sumber
cut
, itu alat yang tepat untuk pekerjaan itu! Jauh lebih bersih dari semua peretas shell itu.Ini bekerja untuk saya:
sumber
Bagaimana dengan pendekatan ini:
Sumber
sumber
IFS";" && Array=($IN)
$'...'
:IN=$'[email protected];[email protected];bet <d@\ns* kl.com>'
. Kemudianecho "${Array[2]}"
akan mencetak string dengan baris baru.set -- "$IN"
juga diperlukan dalam kasus ini. Ya, untuk mencegah ekspansi gumpal, solusinya harus mencakupset -f
.Saya pikir AWK adalah perintah terbaik dan efisien untuk menyelesaikan masalah Anda. AWK disertakan secara default di hampir setiap distribusi Linux.
akan memberi
Tentu saja Anda dapat menyimpan setiap alamat email dengan mendefinisikan kembali bidang cetak awk.
sumber
inode=
ke dalam;
misalnya dengansed -i 's/inode\=/\;/g' your_file_to_process
, kemudian menentukan-F';'
ketika menerapkanawk
, harapan yang dapat membantu Anda.sumber
IN="this is first line; this is second line" arrIN=( $( echo "$IN" | sed -e 's/;/\n/g' ) )
akan menghasilkan array 8 elemen dalam hal ini (elemen untuk setiap ruang kata yang dipisahkan), bukan 2 (elemen untuk setiap baris yang dipisahkan titik koma)arrIN=( $( echo "$IN" | sed -e 's/;/\n/g' ) )
untuk mencapai itu, dan untuk saran untuk mengubah IFSIFS=$'\n'
bagi mereka yang mendarat di sini di masa depan dan perlu membagi string yang berisi spasi. (dan mengembalikannya setelah itu). :)Ini juga berfungsi:
Hati-hati, solusi ini tidak selalu benar. Jika Anda melewati "[email protected]" saja, itu akan menetapkannya untuk ADD1 dan ADD2.
sumber
Pandangan berbeda dari jawaban Darron , ini adalah bagaimana saya melakukannya:
sumber
IFS=";"
tugas hanya ada di$(...; echo $IN)
subkulit; inilah mengapa sebagian pembaca (termasuk saya) pada awalnya berpikir itu tidak akan berhasil. Saya berasumsi bahwa semua $ IN disedot oleh ADDR1. Tapi nickjb benar; itu berhasil. Alasannya adalah bahwaecho $IN
perintah mem-parsing argumennya menggunakan nilai saat ini dari $ IFS, tetapi kemudian menggemakannya ke stdout menggunakan pembatas ruang, terlepas dari pengaturan $ IFS. Jadi efek bersihnya adalah seolah-olah seseorang telah memanggilread ADDR1 ADDR2 <<< "[email protected] [email protected]"
(perhatikan input dipisahkan oleh spasi bukan; -disendiri).*
diecho $IN
dengan ekspansi variabel yang tidak dikutip.Di Bash, cara anti peluru, itu akan berfungsi bahkan jika variabel Anda berisi baris baru:
Lihat:
Trik untuk ini bekerja adalah dengan menggunakan
-d
opsiread
(pembatas) dengan pembatas kosong, sehinggaread
dipaksa untuk membaca semua yang diberi makan. Dan kami memberi makanread
dengan tepat isi variabelin
, tanpa garis belakang baru, terima kasihprintf
. Perhatikan bahwa kami juga meletakkan pembatasprintf
untuk memastikan bahwa string yang dilewatiread
memiliki pembatas trailing. Tanpa itu,read
akan memangkas potensi trailing bidang kosong:bidang kosong yang tertinggal dipertahankan.
Perbarui untuk Bash≥4.4
Sejak Bash 4.4, builtin
mapfile
(aliasreadarray
) mendukung-d
opsi untuk menentukan pembatas. Karenanya cara kanonik lainnya adalah:sumber
\n
, spasi, dan*
secara bersamaan. Juga, tidak ada loop; variabel array dapat diakses di shell setelah eksekusi (bertentangan dengan jawaban tertinggi yang dipilih). Catatan,,in=$'...'
itu tidak bekerja dengan tanda kutip ganda. Saya pikir, perlu lebih banyak upvotes.Bagaimana dengan liner satu ini, jika Anda tidak menggunakan array:
sumber
read -r ...
untuk menggunakan untuk memastikan bahwa, misalnya, dua karakter "\ t" di input berakhir sebagai dua karakter yang sama dalam variabel Anda (bukan karakter tab tunggal).echo "ADDR1 $ADDR1"\n echo "ADDR2 $ADDR2"
ke cuplikan Anda akan menampilkanADDR1 [email protected] [email protected]\nADDR2
(\ n adalah baris baru)IFS
dan di sini string yang diperbaiki dibash
4.3. Mengutip$IN
harus memperbaikinya. (Secara teori,$IN
tidak tunduk pada pemisahan kata atau penggumpalan setelah diperluas, artinya tanda kutip tidak perlu. Bahkan dalam 4.3, meskipun, setidaknya ada satu bug yang tersisa - dilaporkan dan dijadwalkan diperbaiki - sehingga mengutip tetap bagus ide.)Tanpa mengatur IFS
Jika Anda hanya memiliki satu titik dua, Anda dapat melakukannya:
kamu akan mendapatkan:
sumber
Ini adalah 3-liner bersih:
di mana
IFS
kata-kata pembatas berdasarkan pada pemisah dan()
digunakan untuk membuat array . Kemudian[@]
digunakan untuk mengembalikan setiap item sebagai kata yang terpisah.Jika Anda memiliki kode setelah itu, Anda juga harus mengembalikan
$IFS
, misunset IFS
.sumber
$in
kutip memungkinkan wildcard diperluas.Fungsi Bash / zsh berikut membagi argumen pertamanya pada pembatas yang diberikan oleh argumen kedua:
Misalnya, perintahnya
hasil panen
Output ini dapat, misalnya, disalurkan ke perintah lain. Contoh:
Dibandingkan dengan solusi lain yang diberikan, yang ini memiliki keunggulan sebagai berikut:
IFS
tidak diganti: Karena pelingkupan dinamis dari variabel lokal, penimpaanIFS
atas loop menyebabkan nilai baru bocor ke panggilan fungsi yang dilakukan dari dalam loop.Array tidak digunakan: Membaca string ke dalam array menggunakan
read
membutuhkan flag-a
di Bash dan-A
di zsh.Jika diinginkan, fungsi dapat dimasukkan ke dalam skrip sebagai berikut:
sumber
help read
:-d delim continue until the first character of DELIM is read, rather than newline
Anda dapat menerapkan awk ke banyak situasi
Anda juga bisa menggunakan ini
sumber
Ada cara sederhana dan cerdas seperti ini:
Tetapi Anda harus menggunakan gnu xargs, BSD xargs tidak dapat mendukung -d delim. Jika Anda menggunakan apple mac seperti saya. Anda dapat menginstal gnu xargs:
kemudian
sumber
Ini adalah cara paling sederhana untuk melakukannya.
sumber
Ada beberapa jawaban keren di sini (errator esp.), Tetapi untuk sesuatu yang analog untuk dipecah dalam bahasa lain - yang saya maksud dengan pertanyaan aslinya - saya memutuskan untuk ini:
Sekarang
${a[0]}
,${a[1]}
dll, seperti yang Anda harapkan. Gunakan${#a[*]}
untuk sejumlah istilah. Atau untuk beralih, tentu saja:CATATAN PENTING:
Ini bekerja dalam kasus di mana tidak ada ruang untuk dikhawatirkan, yang memecahkan masalah saya, tetapi mungkin tidak menyelesaikan masalah Anda. Pergilah dengan
$IFS
solusi dalam hal itu.sumber
IN
berisi lebih dari dua alamat email. Silakan merujuk ke ide yang sama (tetapi tetap) pada jawaban palindrom${IN//;/ }
(double slash) untuk membuatnya juga berfungsi dengan lebih dari dua nilai. Berhati-hatilah karena setiap wildcard (*?[
) akan diperluas. Dan bidang kosong yang tertinggal akan dibuang.Keluaran
Sistem: Ubuntu 12.04.1
sumber
read
sini dan karenanya dapat mengacaukan sisa kode, jika ada.Jika tidak ada ruang, Kenapa tidak?
sumber
Gunakan
set
built-in untuk memuat$@
array:Lalu, biarkan pesta dimulai:
sumber
set -- $IN
untuk menghindari beberapa masalah dengan "$ IN" dimulai dengan tanda hubung. Namun, ekspansi tanda kutip yang tidak dikutip$IN
akan memperluas wildcard (*?[
).Dua alternatif bourne-ish di mana tidak memerlukan array bash:
Kasus 1 : Tetap bagus dan sederhana: Gunakan NewLine sebagai Pemisah-Rekam ... mis.
Catatan: dalam kasus pertama ini tidak ada sub-proses yang bercabang dua untuk membantu dengan manipulasi daftar.
Ide: Mungkin perlu menggunakan NL secara internal , dan hanya mengonversi ke RS yang berbeda saat menghasilkan hasil akhir secara eksternal .
Kasus 2 : Menggunakan ";" sebagai pemisah rekaman ... mis.
Dalam kedua kasus, sub-daftar dapat dikomposisikan dalam loop tetap-menerus setelah loop selesai. Ini berguna saat memanipulasi daftar dalam memori, alih-alih menyimpan daftar dalam file. {ps tetap tenang dan lanjutkan B-)}
sumber
Terlepas dari jawaban fantastis yang sudah disediakan, jika itu hanya masalah mencetak data yang dapat Anda pertimbangkan untuk menggunakan
awk
:Ini mengatur pemisah bidang ke
;
, sehingga dapat mengulangi bidang denganfor
lingkaran dan mencetak sesuai.Uji
Dengan input lain:
sumber
Di shell Android, sebagian besar metode yang diusulkan tidak berfungsi:
Apa yang berhasil adalah:
di mana
//
berarti penggantian global.sumber
Keluaran:
Penjelasan: Penugasan sederhana menggunakan tanda kurung () mengubah daftar yang dipisahkan titik koma ke dalam array asalkan Anda memiliki IFS yang benar saat melakukan itu. Loop FOR standar menangani masing-masing item dalam array itu seperti biasa. Perhatikan bahwa daftar yang diberikan untuk variabel IN harus "keras" dikutip, yaitu dengan kutu tunggal.
IFS harus disimpan dan dipulihkan karena Bash tidak memperlakukan penugasan dengan cara yang sama seperti perintah. Solusi alternatif adalah untuk membungkus tugas di dalam suatu fungsi dan memanggil fungsi itu dengan IFS yang dimodifikasi. Dalam hal itu tidak diperlukan penyimpanan / pemulihan IFS secara terpisah. Terima kasih untuk "Bize" karena menunjukkannya.
sumber
!"#$%&/()[]{}*? are no problem
baik ... tidak cukup:[]*?
adalah karakter glob. Jadi bagaimana dengan membuat direktori dan file ini: `mkdir '!" # $% &'; Touch '! "# $% & / () [] {} Membuat Anda hahahaha - tidak ada masalah' dan menjalankan perintah Anda? Sederhana mungkin indah, tetapi ketika rusak, itu rusak.mkdir '!"#$%&'; touch '!"#$%&/()[]{} got you hahahaha - are no problem'
. Mereka hanya akan membuat direktori dan file, dengan nama yang tampak aneh, harus saya akui. Kemudian jalankan perintah Anda dengan tepatIN
yang Anda berikan:IN='[email protected];[email protected];Charlie Brown <[email protected];!"#$%&/()[]{}*? are no problem;simple is beautiful :-)'
. Anda akan melihat bahwa Anda tidak akan mendapatkan output yang Anda harapkan. Karena Anda menggunakan subjek metode untuk ekspansi pathname untuk membagi string Anda.*
,?
,[...]
dan bahkan, jikaextglob
diatur,!(...)
,@(...)
,?(...)
,+(...)
yang masalah dengan metode ini!Oke teman-teman!
Inilah jawaban saya!
Mengapa pendekatan ini "yang terbaik" bagi saya?
Karena dua alasan:
[]
sumber
/etc/os-release
dan/etc/lsb-release
dimaksudkan untuk bersumber, dan tidak diuraikan. Jadi metode Anda benar-benar salah. Selain itu, Anda tidak cukup menjawab pertanyaan tentang memiringkan string pada pembatas.Satu baris untuk memisahkan string yang dipisahkan oleh ';' menjadi sebuah array adalah:
Ini hanya menetapkan IFS dalam sebuah subkulit, sehingga Anda tidak perlu khawatir tentang menyimpan dan mengembalikan nilainya.
sumber
0: [email protected];[email protected]\n 1:
(\ n adalah baris baru)$IN
dikutip sehingga tidak mengalami pemisahan IFS. 3. Substitusi proses dipisahkan oleh spasi, tetapi ini dapat merusak data asli.Mungkin bukan solusi yang paling elegan, tetapi bekerja dengan
*
dan spasi:Keluaran
Contoh lain (pembatas di awal dan akhir):
Pada dasarnya ia menghilangkan setiap karakter selain dari
;
membuatdelims
mis.;;;
. Maka itufor
loop dari1
ke yangnumber-of-delimiters
dihitung oleh${#delims}
. Langkah terakhir adalah mendapatkan$i
bagian yang aman menggunakancut
.sumber