Jika saya memiliki array seperti ini di Bash:
FOO=( a b c )
Bagaimana cara saya bergabung dengan elemen dengan koma? Misalnya memproduksi a,b,c
.
Solusi penulisan ulang oleh Pascal Pilz sebagai fungsi dalam Bash 100% murni (tanpa perintah eksternal):
function join_by { local IFS="$1"; shift; echo "$*"; }
Sebagai contoh,
join_by , a "b c" d #a,b c,d
join_by / var local tmp #var/local/tmp
join_by , "${FOO[@]}" #a,b,c
Atau, kita dapat menggunakan printf untuk mendukung pembatas multi-karakter, menggunakan ide oleh @gniourf_gniourf
function join_by { local d=$1; shift; echo -n "$1"; shift; printf "%s" "${@/#/$d}"; }
Sebagai contoh,
join_by , a b c #a,b,c
join_by ' , ' a b c #a , b , c
join_by ')|(' a b c #a)|(b)|(c
join_by ' %s ' a b c #a %s b %s c
join_by $'\n' a b c #a<newline>b<newline>c
join_by - a b c #a-b-c
join_by '\' a b c #a\b\c
konsolebox
gaya :)function join { local IFS=$1; __="${*:2}"; }
ataufunction join { IFS=$1 eval '__="${*:2}"'; }
. Kemudian gunakan__
setelah itu. Ya, saya yang mempromosikan penggunaan__
sebagai variabel hasil;) (dan variabel iterasi umum atau variabel sementara). Jika konsepnya sampai ke situs wiki Bash yang populer, mereka menyalin saya :)$d
dalam penentu formatprintf
. Anda pikir Anda aman karena Anda "lolos"%
tetapi ada peringatan lain: ketika pembatas berisi backslash (misalnya,\n
) atau ketika pembatas dimulai dengan tanda hubung (dan mungkin orang lain saya tidak bisa memikirkan sekarang). Tentu saja Anda dapat memperbaikinya (ganti backslash dengan double backslash dan gunakanprintf -- "$d%s"
), tetapi pada titik tertentu Anda akan merasa bahwa Anda berjuang melawan shell alih-alih bekerja dengannya. Itu sebabnya, dalam jawaban saya di bawah ini, saya menambahkan pembatas pada persyaratan yang harus diikuti.Solusi lain:
Sunting: sama tetapi untuk pemisah panjang variabel multi-karakter:
sumber
printf -v bar ",%s" "${foo[@]}"
. Ini satufork
kurang (sebenarnyaclone
). Hal ini bahkan forking membaca file:printf -v bar ",%s" $(<infile)
.$separator
tidak mengandung%s
atau seperti itu, Anda dapat membuat Andaprintf
kuat:printf "%s%s" "$separator" "${foo[@]}"
.printf "%s%s"
akan menggunakan separator pada contoh pertama HANYA mengatur output, dan kemudian hanya menyatukan sisa argumen.printf "%s" "${foo[@]/#/$separator}"
.IFS=; regex="${foo[*]/#/$separator}"
. Pada titik ini, ini pada dasarnya menjadi jawaban gniourf_gniourf yang IMO lebih bersih dari awal, yaitu menggunakan fungsi untuk membatasi cakupan perubahan IFS dan temp vars.sumber
bar=$( IFS=, ; echo "${foo[*]}" )
@
alih-alih*
, seperti pada$(IFS=, ; echo "${foo[@]}")
? Saya dapat melihat bahwa*
sudah mempertahankan spasi putih dalam elemen, sekali lagi tidak yakin bagaimana, karena@
biasanya diperlukan untuk kepentingan ini.*
. Di halaman bash man, cari "Parameter Khusus" dan cari penjelasan di sebelah*
:Mungkin, misalnya,
sumber
echo "-${IFS}-"
(kurung kurawal memisahkan tanda hubung dari nama variabel).echo $IFS
melakukan hal yang sama.Anehnya solusi saya belum diberikan :) Ini adalah cara paling sederhana untuk saya. Tidak perlu fungsi:
Catatan: Solusi ini diamati bekerja dengan baik dalam mode non-POSIX. Dalam mode POSIX , elemen-elemen masih bergabung dengan benar, tetapi
IFS=,
menjadi permanen.sumber
Berikut adalah fungsi Bash 100% murni yang berfungsi:
Lihat:
Ini bahkan mempertahankan baris baru yang tertinggal, dan tidak perlu subkulit untuk mendapatkan hasil dari fungsinya. Jika Anda tidak menyukai
printf -v
(mengapa Anda tidak menyukainya?) Dan memberikan nama variabel, tentu saja Anda dapat menggunakan variabel global untuk string yang dikembalikan:sumber
join_ret
variabel lokal, dan kemudian menggemakannya di akhir. Ini memungkinkan join () untuk digunakan dengan cara scripting shell yang biasa, misalnya$(join ":" one two three)
, dan tidak memerlukan variabel global.$(...)
trim mengikuti baris baru; jadi jika bidang terakhir array berisi baris baru, ini akan dipangkas (lihat demo di mana mereka tidak dipangkas dengan desain saya).Ini tidak terlalu berbeda dari solusi yang ada, tetapi menghindari menggunakan fungsi terpisah, tidak memodifikasi
IFS
di shell induk dan semuanya dalam satu baris:yang menghasilkan
Batasan: pemisah tidak boleh lebih dari satu karakter.
sumber
Tidak menggunakan perintah eksternal:
Peringatan, ini mengasumsikan elemen tidak memiliki spasi putih.
sumber
echo ${FOO[@]} | tr ' ' ','
Saya akan menggema array sebagai string, kemudian mengubah spasi menjadi feed garis, dan kemudian gunakan
paste
untuk bergabung dengan semuanya dalam satu baris seperti ini:tr " " "\n" <<< "$FOO" | paste -sd , -
Hasil:
a,b,c
Ini sepertinya yang tercepat dan terbersih untukku!
sumber
$FOO
hanya elemen pertama dari array. Juga, ini memecah untuk elemen array yang berisi spasi.Dengan menggunakan kembali solusi @ tidak penting, tetapi dengan satu pernyataan dengan menghindari substitusi $ {: 1} dan kebutuhan variabel perantara.
printf memiliki 'String format digunakan kembali sesering yang diperlukan untuk memenuhi argumen.' di halaman manualnya, sehingga rangkaian string didokumentasikan. Maka triknya adalah menggunakan panjang LIST untuk memotong sperator terakhir, karena cut hanya akan mempertahankan panjang LIST saat field menghitung.
sumber
sumber
@Q
bisa lepas dari nilai-nilai yang digabungkan dari kesalahan interpretasi ketika mereka memiliki sebuah joiner di dalamnya:foo=("a ," "b ' ' c" "' 'd e" "f " ";" "ls -latr"); s=$(IFS=, eval 'echo "${foo[*]@Q}"'); echo "${s}"
keluaran'a ,','b '\'' '\'' c',''\'' '\''d e','f ',';','ls -latr '
solusi printf yang menerima pemisah dengan panjang berapa pun (berdasarkan @ tidak penting jawab)
sumber
printf
%s
$sep
sep
dapat dibersihkan dengan${sep//\%/%%}
. Saya suka solusi Anda lebih baik daripada${bar#${sep}}
atau${bar%${sep}}
(alternatif). Ini bagus jika dikonversi ke fungsi yang menyimpan hasil ke variabel generik seperti__
, dan bukanecho
itu.function join_by { printf -v __ "${1//\%/%%}%s" "${@:2}"; __=${__:${#1}}; }
sumber
HISTSIZE=0
?paste -sd,
bukan tentang penggunaan sejarah.HISTSIZE=0
- coba saja.Versi pendek dari jawaban teratas:
Pemakaian:
sumber
join_strings () { local d="$1"; echo -n "$2"; shift 2 && printf '%s' "${@/#/$d}"; }
join_strings () { local d="$1"; echo -n "$2"; shift 2 && printf '$d%s' "${@}"; }
Ini berfungsi dengan penggunaan:join_strings 'delim' "${array[@]}"
atau tidakjoin_strings 'delim' ${array[@]}
Kombinasikan yang terbaik dari semua dunia sejauh ini dengan mengikuti ide.
Karya kecil ini adalah
Contoh:
sumber
join_ws ,
(tanpa argumen) salah output,,
. 2.join_ws , -e
salah menghasilkan apa-apa (itu karena Anda salah menggunakan,echo
bukanprintf
). Saya sebenarnya tidak tahu mengapa Anda mengiklankan penggunaanecho
alih-alihprintf
:echo
terkenal rusak, danprintf
merupakan bawaan yang kuat.Saat ini saya sedang menggunakan:
Yang berhasil, tetapi (dalam kasus umum) akan pecah mengerikan jika elemen array memiliki ruang di dalamnya.
(Bagi mereka yang tertarik, ini adalah skrip pembungkus di sekitar pep8.py )
sumber
ARGS="--ignore $(echo "${TO_IGNORE[@]}" | tr ' ' ',')"
. Operator$()
lebih kuat daripada backtics (memungkinkan bersarang$()
dan""
). Membungkus${TO_IGNORE[@]}
dengan tanda kutip ganda juga akan membantu.Usaha saya.
sumber
Gunakan perl untuk pemisah multicharacter:
Atau dalam satu baris:
sumber
join
namanya bertentangan dengan beberapa omong kosong diOS X
.. saya akan menyebutnyaconjoined
, atau mungkinjackie_joyner_kersee
?Terima kasih @gniourf_gniourf untuk komentar terperinci tentang kombinasi dunia terbaik saya sejauh ini. Maaf untuk kode pengiriman yang tidak dirancang dan diuji secara menyeluruh. Ini adalah upaya yang lebih baik.
Kecantikan ini menurut konsepsi adalah
Contoh tambahan:
sumber
Jika elemen yang ingin Anda gabungkan bukan array hanya string yang dipisahkan spasi, Anda bisa melakukan sesuatu seperti ini:
misalnya, kasus penggunaan saya adalah bahwa beberapa string dilewatkan dalam skrip shell saya dan saya perlu menggunakan ini untuk berjalan pada query SQL:
Dalam my_script, saya perlu melakukan "SELECT * FROM table WHERE name IN ('aa', 'bb', 'cc', 'dd'). Kemudian perintah di atas akan berguna.
sumber
printf -v bar ...
daripada harus menjalankan loop printf di subkulit dan menangkap output.Inilah salah satu yang didukung sebagian besar kerang POSIX:
sumber
local
) sama sekali.Menggunakan tipuan variabel untuk merujuk langsung ke array juga berfungsi. Referensi yang dinamai juga dapat digunakan, tetapi hanya tersedia dalam 4.3.
Keuntungan menggunakan bentuk fungsi ini adalah Anda dapat memiliki pemisah opsional (default ke karakter pertama default
IFS
, yang merupakan spasi; mungkin menjadikannya string kosong jika Anda mau), dan menghindari perluasan nilai dua kali (pertama ketika dilewatkan sebagai parameter, dan kedua sebagai"$@"
di dalam fungsi).Solusi ini juga tidak mengharuskan pengguna untuk memanggil fungsi di dalam substitusi perintah - yang memanggil subkulit, untuk mendapatkan versi gabungan dari string yang ditugaskan ke variabel lain.
Jangan ragu untuk menggunakan nama yang lebih nyaman untuk fungsi ini.
Ini bekerja dari 3.1 hingga 5.0-alpha. Seperti yang diamati, tipuan variabel tidak hanya bekerja dengan variabel tetapi dengan parameter lain juga.
Array dan elemen array juga merupakan parameter (entitas yang menyimpan nilai), dan referensi ke array juga secara teknis merujuk ke parameter. Dan sangat mirip dengan parameter khusus
@
,array[@]
juga membuat referensi yang valid.Bentuk ekspansi yang diubah atau selektif (seperti ekspansi substring) yang menyimpang referensi dari parameter itu sendiri tidak lagi berfungsi.
Memperbarui
Dalam versi rilis Bash 5.0, indirection variabel sudah disebut ekspansi tidak langsung dan perilakunya sudah secara eksplisit didokumentasikan dalam manual:
Mencatat bahwa dalam dokumentasi
${parameter}
,parameter
disebut sebagai "parameter shell seperti yang dijelaskan (dalam) PARAMETER atau referensi array ". Dan dalam dokumentasi array, disebutkan bahwa "Setiap elemen array dapat dirujuk menggunakan${name[subscript]}
". Ini membuat__r[@]
referensi array.Bergabung dengan versi argumen
Lihat komentar saya dalam jawaban Riccardo Galli .
sumber
__
sebagai nama variabel? Membuat kode benar-benar tidak dapat dibaca.Pendekatan ini menangani ruang di dalam nilai, tetapi membutuhkan loop:
sumber
Jika Anda membuat array dalam satu lingkaran, berikut adalah cara sederhana:
sumber
x=${"${arr[*]}"// /,}
Ini adalah cara terpendek untuk melakukannya.
Contoh,
sumber
bash: ${"${arr[*]}"// /,}: bad substitution
Mungkin terlambat untuk pesta, tetapi ini berhasil untuk saya:
sumber
Mungkin saya kehilangan sesuatu yang jelas, karena saya adalah pemula dalam hal bash / zsh, tetapi bagi saya sepertinya Anda tidak perlu menggunakan
printf
sama sekali. Juga tidak akan benar-benar jelek untuk dilakukan tanpanya.Setidaknya, itu telah bekerja untuk saya sejauh ini tanpa masalah.
Sebagai contoh,,
join \| *.sh
yang, katakanlah saya di~
direktori saya , outpututilities.sh|play.sh|foobar.sh
. Cukup baik untukku.EDIT: Ini pada dasarnya adalah jawaban Nil Geisweiller , tetapi digeneralisasi menjadi suatu fungsi.
sumber
Ini menangani koma ekstra di akhir juga. Saya bukan ahli bash. Hanya 2c saya, karena ini lebih sederhana dan dapat dimengerti
sumber
atau
sumber