Manipulasi piping bash string

9

Saya sudah membaca beberapa pertanyaan manipulasi piping bash string tetapi mereka tampaknya aplikasi khusus.

Pada dasarnya, apakah ada cara untuk melakukan hal di bawah ini dengan lebih sederhana?

dari pada

$ string='hello world'; string2="${string// /_}"; echo "${string2^^}"
HELLO_WORLD

sesuatu seperti

$ echo 'hello world' | $"{-// /_}" | "${ -^^}"
HELLO_WORLD

Sunting Saya tertarik untuk tetap berada dalam manipulasi bash jika memungkinkan untuk mempertahankan kecepatan (bukan sed / awk yang memiliki kecenderungan untuk sangat memperlambat skrip saya)

Sunting2: @jimmij

Saya suka contoh kedua dan menuntun saya untuk membuat suatu fungsi.

bash_m() { { read x; echo "${x// /_}"; } | { read x; echo "${x^^}"; }; }
echo hello world | bash_m
HELLO_WORLD
Miati
sumber
1
menurut Anda mengapa sed / awk akan lambat untuk tujuan ini? Mereka secepat mereka datang.
mkc
1
@Ketan Sed dan awk adalah proses yang terpisah, sehingga mereka tidak akan pernah bisa secepat sesuatu yang bisa dilakukan bash secara asli tanpa meluncurkan proses terpisah. Biasanya perbedaan ini hampir tidak terlihat, tetapi di mana kinerja penting dalam skrip shell biasanya loop atau komputasi tertentu sedang diulangi dalam jumlah yang sangat besar, dan menelurkan ribuan proses akan terasa lebih lambat daripada melakukan manipulasi string sederhana di bash secara asli.
jw013
2
@ jw013 Ini berlaku untuk string pendek sebagai "halo dunia" dari pertanyaan, tetapi jika string sangat panjang, katakan trmanual, maka kebalikannya benar karena waktu pemijahan proses dapat diabaikan dibandingkan dengan waktu manipulasi string yang seddan awkberdedikasi. Jika string sangat panjang, katakan manual bash keseluruhan, maka bash bisa menolak untuk melanjutkan sama sekali, karena beberapa batasan internal.
jimmij
2
@ jw013 Saya mengklaim kode manipulasi string yang bash kurang efisien alat kemudian didedikasikan sebagai sed, awk, tratau serupa. Lihatlah jawaban gena2x, yang saya edit beberapa waktu lalu dengan menambahkan informasi ini: unix.stackexchange.com/questions/162221/... Anda mungkin ingin membandingkannya dengan jawaban terdon untuk pertanyaan yang sama di mana ia memberikan waktu untuk string pendek di mana proses kasus pemijahan membutuhkan waktu paling lama. Anda dapat mengujinya sendiri dan memposting hasilnya.
jimmij
1
@Miati Mengapa Anda berpikir ekstra read x; echo $xini lebih baik untuk kinerja? Sintaksnya tidak terlihat lebih pendek atau lebih bersih. x=${x// /_}; x=${x^^}adalah cara yang jauh lebih ringkas untuk melakukan hal yang sama seperti {read x; echo ${x.... Sejauh kinerja berjalan, @jimmij telah menunjukkan bahwa tr/ sedakan lebih cepat daripada bash, perhitungan garpu sama. Menggunakan pipa selalu menghasilkan proses ekstra sehingga argumen menyimpan garpu tidak lagi berlaku. Jadi, jika menggunakan pipa, gunakan sed/ trdll. Jika Anda dapat melakukannya di bash, lakukan dan lewati read x; echo $xomong kosong ini .
jw013

Jawaban:

9

Apa kata jimmij. Contoh terakhirnya adalah yang paling dekat dengan apa yang Anda coba dalam ekspresi pipa Anda.

Inilah varian dari tema itu:

echo 'hello world'|echo $(read s;s=${s^^};echo ${s// /_})

Saya akan cenderung menggunakan tr, karena cukup cepat.

echo 'hello world'|tr ' [:lower:]' '_[:upper:]'

Saya kira itu memalukan bahwa bash tidak mengizinkan ekspansi parameter bersarang; OTOH, penggunaan ekspresi bersarang seperti itu dapat dengan mudah menyebabkan kode yang menyakitkan untuk dibaca. Kecuali Anda benar - benar membutuhkan hal-hal untuk berjalan secepat mungkin, lebih baik menulis kode yang mudah dibaca, dipahami, dan dipelihara, daripada kode yang tampak pintar yang merupakan PITA untuk di-debug. Dan jika Anda benar - benar membutuhkan hal-hal yang harus dilakukan dengan kecepatan tinggi, Anda harus menggunakan kode kompilasi, bukan skrip.

PM 2Ring
sumber
7

Anda tidak dapat melewatkan ekspansi parameter sedemikian rupa. Ketika Anda merujuk xmenggunakan $simbol seperti dalam "${x}"formulir, maka itu harus nama variabel nyata, bukan input standar, setidaknya tidak dalam bash. Di dalam zshAnda dapat melakukan substitusi parameter bersarang dengan cara berikut:

$ x=''hello world'
$ echo ${${x// /_}:u}
HELLO_WORLD

(catatan: :uadalah untuk zshsama seperti ^^untuk bash)

Bersarang di bash tidak mungkin dan saya pikir apa yang Anda tulis dalam pertanyaan adalah yang terbaik yang bisa didapat, tetapi jika karena alasan aneh Anda perlu melibatkan pipa ke dalam persamaan maka Anda mungkin ingin mencoba ini:

$ echo 'hello world' | { read x; echo "${x// /_}"; } | { read y; echo "${y^^}"; }
HELLO_WORLD
jimmij
sumber
1
Mempertimbangkan pembicaraan kami baru-baru ini tentang bagaimana tr/ sedlebih cepat dari bashpada pemrosesan string, dan mempertimbangkan bagaimana Anda menggunakan pipa untuk melewati string melalui standar I / O, saya benar-benar melihat titik untuk melakukan operasi-operasi tersebut di bash sebagai lawan dari tr/ sed. Mengapa orang yang | { read x; echo $x... }bertentangan dengan | sedyang melakukan hal yang sama?
jw013
1
@ jw013 terus terang saya lihat di samping tidak ada gunanya. Ini hanyalah contoh untuk secara paksa melibatkan pipa ke masalah, karena OP secara eksplisit meminta mereka dan tidak ingin menggunakan program eksternal (keduanya echodan readbash built-in, jadi pada prinsipnya sedikit lebih cepat). Seperti yang sudah saya tulis dalam manipulasi parameter jawaban progresif yang OP miliki dalam pertanyaan adalah yang terbaik yang bisa saya dapatkan dari pendapat saya untuk tugas ini di bash. Pokoknya masalahnya agak akademis.
jimmij