Saya mencoba menemukan cara paling efisien untuk beralih melalui nilai-nilai tertentu yang merupakan jumlah nilai yang konsisten dari satu sama lain dalam daftar kata yang dipisahkan oleh ruang (saya tidak ingin menggunakan array). Sebagai contoh,
list="1 ant bat 5 cat dingo 6 emu fish 9 gecko hare 15 i j"
Jadi saya ingin bisa beralih melalui daftar dan hanya mengakses 1,5,6,9 dan 15.
EDIT: Seharusnya saya menjelaskan bahwa nilai yang saya coba dapatkan dari daftar tidak harus berbeda dalam format dari sisa daftar. Apa yang membuat mereka istimewa adalah semata-mata posisi mereka dalam daftar (Dalam hal ini, posisi 1,4,7 ...). Jadi daftarnya bisa saja1 2 3 5 9 8 6 90 84 9 3 2 15 75 55
tetapi saya masih menginginkan angka yang sama. Dan juga, saya ingin dapat melakukannya dengan asumsi saya tidak tahu panjang daftar.
Metode yang saya pikirkan sejauh ini adalah:
Metode 1
set $list
found=false
find=9
count=1
while [ $count -lt $# ]; do
if [ "${@:count:1}" -eq $find ]; then
found=true
break
fi
count=`expr $count + 3`
done
Metode 2
set list
found=false
find=9
while [ $# ne 0 ]; do
if [ $1 -eq $find ]; then
found=true
break
fi
shift 3
done
Metode 3 Saya cukup yakin perpipaan membuat ini pilihan terburuk, tetapi saya mencoba menemukan metode yang tidak menggunakan set, karena penasaran.
found=false
find=9
count=1
num=`echo $list | cut -d ' ' -f$count`
while [ -n "$num" ]; do
if [ $num -eq $find ]; then
found=true
break
fi
count=`expr $count + 3`
num=`echo $list | cut -d ' ' -f$count`
done
Jadi apa yang paling efisien, atau saya melewatkan metode yang lebih sederhana?
sumber
Jawaban:
Cukup sederhana
awk
. Ini akan memberi Anda nilai setiap bidang keempat untuk input dengan panjang berapa pun:Ini berfungsi memanfaatkan
awk
variabel bawaan sepertiNF
(jumlah bidang dalam catatan), dan melakukan beberapafor
perulangan sederhana untuk beralih di sepanjang bidang untuk memberi Anda yang Anda inginkan tanpa perlu tahu sebelumnya berapa banyak akan ada.Atau, jika Anda memang hanya ingin bidang-bidang tertentu seperti yang ditentukan dalam contoh Anda:
Adapun pertanyaan tentang efisiensi, rute paling sederhana adalah dengan menguji ini atau masing-masing metode Anda yang lain dan gunakan
time
untuk menunjukkan berapa lama waktu yang dibutuhkan; Anda juga bisa menggunakan alat sepertistrace
untuk melihat bagaimana sistem panggilan mengalir. Penggunaantime
terlihat seperti:Anda dapat membandingkan output tersebut antara berbagai metode untuk melihat mana yang paling efisien dalam hal waktu; alat lain dapat digunakan untuk metrik efisiensi lainnya.
sumber
echo
vs<<<
, "identik" adalah kata yang terlalu kuat. Bisa dibilangstuff <<< "$list"
hampir identik denganprintf "%s\n" "$list" | stuff
. Mengenaiecho
vsprintf
, saya mengarahkan Anda ke jawaban ini<<<
menambahkan baris baru di akhir. Ini mirip dengan cara$()
menghapus baris baru dari akhir. Ini karena baris diakhiri oleh baris baru.<<<
mengumpankan ekspresi sebagai garis, sehingga harus diakhiri oleh baris baru."$()"
mengambil baris dan menyediakannya sebagai argumen, jadi masuk akal untuk mengonversi dengan menghapus baris baru yang berhenti.awk
adalah biner yang berdiri sendiri yang harus memulai. Tidak seperti perl atau terutama Python, interpreter awk memulai dengan cepat (masih semua overhead linker dinamis yang biasa membuat cukup banyak panggilan sistem, tetapi awk hanya menggunakan libc / libm dan libdl. Mis. Gunakanstrace
untuk memeriksa panggilan sistem awk startup awk) . Banyak shell (seperti bash) sangat lambat, sehingga menjalankan satu proses awk bisa lebih cepat daripada melompati token dalam daftar dengan built-in shell bahkan untuk ukuran daftar ish kecil. Dan terkadang Anda bisa menulis#!/usr/bin/awk
skrip alih - alih#!/bin/sh
skrip.Aturan pertama optimasi perangkat lunak: Jangan .
Sampai Anda tahu kecepatan program adalah masalah, Anda tidak perlu memikirkan seberapa cepatnya. Jika daftar Anda panjangnya atau hanya ~ 100-1000 item, Anda mungkin tidak akan menyadari berapa lama. Ada kemungkinan Anda menghabiskan lebih banyak waktu untuk memikirkan pengoptimalan daripada apa perbedaannya.
Aturan kedua: Ukur .
Itu cara yang pasti untuk mencari tahu, dan yang memberi jawaban untuk sistem Anda. Terutama dengan kerang, ada begitu banyak, dan mereka tidak semuanya identik. Jawaban untuk satu shell mungkin tidak berlaku untuk Anda.
Dalam program yang lebih besar, pembuatan profil juga dilakukan di sini. Bagian paling lambat mungkin bukan yang Anda pikirkan.
Ketiga, aturan pertama optimasi skrip shell: Jangan gunakan shell .
Ya benar Banyak shell tidak dibuat menjadi cepat (karena meluncurkan program eksternal tidak harus), dan mereka bahkan dapat mengurai baris kode sumber lagi setiap kali.
Gunakan sesuatu seperti awk atau Perl sebagai gantinya. Dalam patokan mikro sepele yang saya lakukan,
awk
adalah puluhan kali lebih cepat daripada shell umum dalam menjalankan loop sederhana (tanpa I / O).Namun, jika Anda menggunakan shell, gunakan fungsi builtin shell bukan perintah eksternal. Di sini, Anda menggunakan
expr
yang tidak dibangun di shell yang saya temukan di sistem saya, tetapi yang dapat diganti dengan ekspansi aritmatika standar. Misalnyai=$((i+1))
alih-alihi=$(expr $i + 1)
menambahi
. Penggunaan Andacut
dalam contoh terakhir mungkin juga dapat diganti dengan ekspansi parameter standar.Lihat juga: Mengapa menggunakan shell loop untuk memproses teks dianggap praktik buruk?
Langkah # 1 dan # 2 seharusnya berlaku untuk pertanyaan Anda.
sumber
awk
loop selalu lebih baik atau lebih buruk daripada loop shell. Shell itu sangat bagus dalam menjalankan perintah dan mengarahkan input dan output ke dan dari proses, dan terus terang agak kikuk di segala hal lain; sementara alat-alat sepertiawk
yang fantastis di pengolahan data teks, karena itu apa kerang dan alat-alat sepertiawk
yang dibuat untuk (masing-masing) di tempat pertama.dash
daripada dengangawk
, dandash
merupakan shell tercepat yang saya uji ...dash
danbusybox
tidak mendukung(( .. ))
- Saya pikir ini adalah ekstensi yang tidak standar.++
juga secara eksplisit disebutkan sebagai tidak wajib, sejauh yang saya tahu,i=$((i+1))
atau: $(( i += 1))
yang aman.Saya hanya akan memberikan beberapa saran umum dalam jawaban ini, dan bukan tolok ukur. Benchmark adalah satu-satunya cara untuk secara andal menjawab pertanyaan tentang kinerja. Tetapi karena Anda tidak mengatakan berapa banyak data yang Anda manipulasi dan seberapa sering Anda melakukan operasi ini, tidak ada cara untuk melakukan benchmark yang berguna. Apa yang lebih efisien untuk 10 item dan apa yang lebih efisien untuk 1000000 item seringkali tidak sama.
Sebagai aturan umum, menjalankan perintah eksternal lebih mahal daripada melakukan sesuatu dengan konstruksi shell murni, selama kode shell murni tidak melibatkan loop. Di sisi lain, perulangan shell yang beriterasi pada string besar atau sejumlah besar string cenderung lebih lambat dari satu permintaan alat tujuan khusus. Misalnya, menjalankan loop Anda
cut
bisa terasa lambat dalam praktik, tetapi jika Anda menemukan cara untuk melakukan semuanya dengan satucut
pemanggilan yang mungkin lebih cepat daripada melakukan hal yang sama dengan manipulasi string dalam shell.Perhatikan bahwa titik batas dapat sangat bervariasi antar sistem. Itu dapat bergantung pada kernel, pada bagaimana penjadwal kernel dikonfigurasikan, pada sistem file yang berisi executable eksternal, pada seberapa banyak tekanan CPU vs memori yang ada saat ini, dan banyak faktor lainnya.
Jangan menelepon
expr
untuk melakukan aritmatika jika Anda benar-benar khawatir tentang kinerja. Bahkan, jangan meneleponexpr
untuk melakukan aritmatika sama sekali. Kerang memiliki aritmatika bawaan, yang lebih jelas dan lebih cepat daripada memohonexpr
.Anda tampaknya menggunakan bash, karena Anda menggunakan konstruksi bash yang tidak ada di sh. Jadi mengapa Anda tidak menggunakan array? Array adalah solusi yang paling alami, dan kemungkinan akan menjadi yang tercepat juga. Perhatikan bahwa indeks array mulai dari 0.
Skrip Anda mungkin lebih cepat jika Anda menggunakan sh, jika sistem Anda memiliki tanda hubung atau ksh
sh
daripada bash. Jika Anda menggunakan sh, Anda tidak mendapatkan array bernama, tetapi Anda masih mendapatkan array salah satu parameter posisi, yang dapat Anda aturset
. Untuk mengakses elemen pada posisi yang tidak diketahui sampai runtime, Anda perlu menggunakaneval
(berhati-hatilah mengutip sesuatu dengan benar!).Jika Anda hanya ingin mengakses array sekali dan bergerak dari kiri ke kanan (melewatkan beberapa nilai), Anda dapat menggunakan
shift
sebagai ganti indeks variabel.Pendekatan mana yang lebih cepat tergantung pada shell dan jumlah elemen.
Kemungkinan lain adalah menggunakan pemrosesan string. Ini memiliki keuntungan karena tidak menggunakan parameter posisi, sehingga Anda dapat menggunakannya untuk hal lain. Ini akan lebih lambat untuk sejumlah besar data, tetapi itu tidak mungkin membuat perbedaan nyata untuk sejumlah kecil data.
sumber
shift && shift && shift
denganshift 3
dalam contoh ketiga Anda - kecuali shell yang Anda gunakan tidak mendukungnya.shift 3
akan gagal jika ada terlalu sedikit argumen yang tersisa. Anda perlu sesuatu sepertiif [ $# -gt 3 ]; then shift 3; else set --; fi
awk
adalah pilihan yang bagus, jika Anda dapat melakukan semua pemrosesan di dalam skrip Awk. Jika tidak, Anda hanya akan mengirim output Awk ke utilitas lain, menghancurkan perolehan kinerjaawk
.bash
iterasi atas array juga bagus, jika Anda bisa memasukkan seluruh daftar Anda ke dalam array (yang untuk shell modern mungkin merupakan jaminan) dan Anda tidak keberatan dengan senam sintaks array.Namun, pendekatan saluran pipa:
Dimana:
xargs
mengelompokkan daftar yang dipisahkan spasi menjadi tiga, masing-masing baris baru dipisahkanwhile read
mengkonsumsi daftar itu dan menampilkan kolom pertama dari setiap grupgrep
memfilter kolom pertama (sesuai dengan setiap posisi ketiga dalam daftar asli)Meningkatkan pemahaman, menurut saya. Orang-orang sudah tahu apa yang dilakukan alat-alat ini, sehingga mudah dibaca dari kiri ke kanan dan alasan tentang apa yang akan terjadi. Pendekatan ini juga secara jelas mendokumentasikan panjang langkah (
-n3
) dan pola filter (9
), sehingga mudah untuk membuat variasi:Ketika kita mengajukan pertanyaan "efisiensi", pastikan untuk memikirkan "efisiensi seumur hidup total". Perhitungan itu mencakup upaya pengelola untuk menjaga agar kode tetap berfungsi, dan kami, kantung daging adalah mesin yang paling tidak efisien di seluruh operasi.
sumber
Mungkin ini
sumber
Jangan gunakan perintah shell jika Anda ingin menjadi efisien. Batasi diri Anda untuk pipa, pengalihan, pergantian dll, dan program. Itu sebabnya
xargs
danparallel
utilitas ada - karena bash sementara loop tidak efisien dan sangat lambat. Gunakan bash loop hanya sebagai resolusi terakhir.Tapi Anda mungkin harus agak lebih cepat dengan yang baik
awk
.sumber
Menurut pendapat saya solusi yang paling jelas (dan mungkin yang paling performan juga) adalah dengan menggunakan variabel awk RS dan ORS:
sumber
Menggunakan skrip shell GNU
sed
dan POSIX :Atau dengan
bash
's substitusi parameter :Non- GNU ( yaitu POSIX )
sed
, danbash
:Atau lebih mudah dibawa, menggunakan POSIX
sed
dan skrip shell:Output dari semua ini:
sumber