Pemrogram secara teratur menggunakan bash untuk menyelesaikan banyak tugas yang berkaitan dengan pengembangan perangkat lunak. Pada saat yang sama, array bash sering dianggap sebagai salah satu fitur yang paling sulit dipahami dari shell ini (mungkin, array adalah yang kedua setelah ekspresi reguler dalam hal ini). Penulis bahan, terjemahan yang kami terbitkan hari ini, mengundang semua orang ke dunia array bash yang indah, yang, jika Anda terbiasa dengan sintaks mereka yang tidak biasa, dapat membawa banyak manfaat.
Tantangan nyata bahwa bash arrays berguna
Menulis tentang bash kontroversial. Faktanya adalah bahwa artikel tentang bash sering berubah menjadi panduan pengguna yang dikhususkan untuk cerita tentang fitur sintaksis dari perintah yang dipermasalahkan. Artikel ini ditulis secara berbeda, kami harap Anda tidak menemukannya sebagai "manual pengguna" berikutnya.
Dengan penjelasan di atas, bayangkan skenario nyata untuk menggunakan array di bash. Misalkan Anda dihadapkan dengan tugas mengevaluasi dan mengoptimalkan utilitas dari seperangkat alat internal baru yang digunakan di perusahaan Anda. Pada langkah pertama penelitian ini, Anda perlu mengujinya dengan set parameter yang berbeda. Tes ini bertujuan untuk mempelajari bagaimana seperangkat alat baru berperilaku ketika mereka menggunakan jumlah utas yang berbeda. Untuk kesederhanaan presentasi, kami menganggap bahwa "kotak alat" adalah "kotak hitam" yang dikompilasi dari kode C ++. Saat menggunakannya, satu-satunya parameter yang dapat kita pengaruhi adalah jumlah utas yang disediakan untuk pemrosesan data. Memanggil sistem yang sedang diselidiki dari baris perintah terlihat seperti ini:
./pipeline
Dasar-dasarnya
Pertama-tama, kita mendeklarasikan sebuah array yang berisi nilai-nilai dari parameter
--threads
yang ingin kita uji sistemnya. Array ini terlihat seperti ini:
allThreads=(1 2 4 8 16 32 64 128)
Dalam contoh ini, semua elemen adalah angka, tetapi pada kenyataannya, dalam bash array, Anda bisa menyimpan angka dan string sekaligus. Misalnya, deklarasi array semacam itu cukup dapat diterima:
myArray=(1 2 "three" 4 "five")
Seperti variabel bash lainnya, pastikan tidak ada spasi di sekitar tanda
=
. Jika tidak, bash akan mempertimbangkan nama variabel nama program yang perlu dijalankan, dan
=
menjadi argumen pertamanya!
Sekarang kita telah menginisialisasi array, mari kita ekstrak beberapa elemen darinya. Di sini Anda dapat melihat, misalnya, bahwa
echo $allThreads
akan menampilkan hanya elemen pertama dari array.
Untuk memahami alasan perilaku ini, mari kita sedikit menyimpang dari array dan mengingat cara bekerja dengan variabel dalam bash. Perhatikan contoh berikut:
type="article" echo "Found 42 $type"
Misalkan Anda memiliki variabel
$type
yang berisi string yang mewakili kata benda. Setelah kata ini, tambahkan huruf
s
. Namun, Anda tidak dapat hanya menambahkan surat ini ke akhir nama variabel, karena ini akan mengubah perintah untuk mengakses variabel menjadi
$types
, artinya, kami akan bekerja dengan variabel yang sama sekali berbeda. Dalam situasi ini, Anda dapat menggunakan konstruk seperti
echo "Found 42 "$type"s"
. Tapi yang terbaik untuk menyelesaikan masalah ini menggunakan kurung kurawal:
echo "Found 42 ${type}s"
, yang memungkinkan kita memberi tahu bash di mana nama variabel dimulai dan berakhir (menariknya, sintaks yang sama digunakan dalam JavaScript ES6 untuk menanamkan variabel dalam ekspresi dalam
string pola ).
Sekarang kembali ke array. Ternyata meskipun kurung kurawal biasanya tidak diperlukan ketika bekerja dengan variabel, mereka diperlukan untuk bekerja dengan array. Mereka memungkinkan Anda untuk mengatur indeks untuk mengakses elemen-elemen array. Sebagai contoh, perintah dari form
echo ${allThreads[1]}
akan menampilkan elemen kedua dari array. Jika Anda lupa tentang kurung kurawal pada konstruksi di atas, bash akan menganggap
[1]
sebagai string dan memproses apa yang terjadi.
Seperti yang Anda lihat, array di bash memiliki sintaks aneh, tetapi di dalamnya, setidaknya, penomoran elemen dimulai dari awal. Ini membuatnya mirip dengan array dari banyak bahasa pemrograman lain.
Cara mengakses elemen array
Dalam contoh di atas, kami menggunakan indeks integer dalam array yang ditentukan secara eksplisit. Sekarang pertimbangkan dua cara lagi untuk bekerja dengan array.
Metode pertama berlaku jika kita membutuhkan elemen
$i
array, di mana
$i
adalah variabel yang berisi indeks elemen array yang diinginkan. Anda bisa mengekstrak elemen ini dari array menggunakan konstruk dari form
echo ${allThreads[$i]}
.
Metode kedua memungkinkan Anda untuk menampilkan semua elemen array. Ini terdiri dalam mengganti indeks numerik dengan simbol
@
(dapat diartikan sebagai perintah yang menunjuk ke semua elemen dari array). Ini terlihat seperti ini:
echo ${allThreads[@]}
.
Iterasi elemen array dalam loop
Prinsip-prinsip di atas bekerja dengan elemen array akan berguna bagi kita untuk memecahkan masalah penghitungan elemen array. Dalam kasus kami, ini berarti memulai perintah
pipeline
sedang dipelajari dengan masing-masing nilai, yang melambangkan jumlah utas dan disimpan dalam array. Ini terlihat seperti ini:
for t in ${allThreads[@]}; do ./pipeline --threads $t done
Menghitung Indeks Array di Loop
Sekarang pertimbangkan pendekatan yang sedikit berbeda untuk menyortir array. Alih-alih mengulangi elemen, kita bisa mengulangi indeks array:
for i in ${!allThreads[@]}; do ./pipeline --threads ${allThreads[$i]} done
Mari kita menganalisis apa yang terjadi di sini. Seperti yang telah kita lihat, konstruksi form
${allThreads[@]}
mewakili semua elemen array. Ketika kami menambahkan tanda seru di sini, kami mengubah konstruksi ini menjadi
${!allThreads[@]}
, yang mengarah pada fakta bahwa ia mengembalikan indeks array (dari 0 hingga 7 dalam kasus kami).
Dengan kata lain,
for
loop
for
semua indeks array yang direpresentasikan sebagai variabel
$i
, dan di dalam body loop, elemen-elemen array yang berfungsi sebagai nilai-nilai dari parameter
--thread
menggunakan
${allThreads[$i]}
construct.
Membaca kode ini lebih sulit daripada yang ada pada contoh sebelumnya. Karena itu, muncul pertanyaan untuk apa semua kesulitan ini. Dan kami membutuhkan ini karena dalam beberapa situasi, saat memproses array dalam loop, Anda perlu mengetahui indeks dan nilai-nilai elemen. Misalnya, jika Anda perlu melewati elemen pertama dari array, iterasi dari indeks akan menyelamatkan kita, misalnya, dari kebutuhan untuk membuat variabel tambahan dan dari menambahkannya dalam satu lingkaran untuk bekerja dengan elemen array.
Mengisi Array
Sejauh ini, kami telah menjelajahi sistem dengan menjalankan perintah
pipeline
dan memberikannya setiap nilai dari parameter
--threads
menarik bagi kami. Sekarang anggaplah bahwa perintah ini memberikan durasi proses tertentu dalam detik. Kami ingin mencegat data yang dikembalikan ke sana pada setiap iterasi dan menyimpannya dalam array lain. Ini akan memberi kita kesempatan untuk bekerja dengan data yang disimpan setelah semua tes berakhir.
Konstruksi sintaks yang berguna
Sebelum kita berbicara tentang cara menambahkan data ke array, mari kita lihat beberapa konstruksi sintaks yang berguna. Untuk memulainya, kita membutuhkan mekanisme untuk memperoleh output data dengan perintah bash. Untuk menangkap output dari suatu perintah, Anda perlu menggunakan konstruksi berikut:
output=$( ./my_script.sh )
Setelah menjalankan perintah ini,
$output
myscript.sh
apa yang akan disimpan dalam variabel
$output
.
Konstruksi kedua, yang akan segera berguna, memungkinkan kita untuk melampirkan data baru ke array. Ini terlihat seperti ini:
myArray+=( "newElement1" "newElement2" )
Pemecahan masalah
Sekarang, jika Anda mengumpulkan semua yang baru saja kami pelajari, Anda dapat membuat skrip untuk menguji sistem, yang mengeksekusi perintah dengan masing-masing nilai parameter dari array dan menyimpan di array lain apa yang ditampilkan perintah ini.
allThreads=(1 2 4 8 16 32 64 128) allRuntimes=() for t in ${allThreads[@]}; do runtime=$(./pipeline --threads $t) allRuntimes+=( $runtime ) done
Apa selanjutnya
Kami baru saja memeriksa cara menggunakan bash array untuk beralih pada parameter yang digunakan saat memulai program dan untuk menyimpan data yang dikembalikan oleh program ini. Namun, opsi untuk menggunakan array tidak terbatas pada skenario ini. Berikut adalah beberapa contoh lagi.
Lansiran Masalah
Dalam skenario ini, kita akan melihat aplikasi yang dipecah menjadi modul. Masing-masing modul memiliki file log sendiri. Kami dapat menulis skrip pekerjaan
cron
yang, jika ditemukan masalah dalam file log yang sesuai, akan memberi tahu melalui email orang yang bertanggung jawab untuk masing-masing modul:
# - logPaths=("api.log" "auth.log" "jenkins.log" "data.log") logEmails=("jay@email" "emma@email" "jon@email" "sophia@email") # for i in ${!logPaths[@]}; do log=${logPaths[$i]} stakeholder=${logEmails[$i]} numErrors=$( tail -n 100 "$log" | grep "ERROR" | wc -l ) # 5 if [[ "$numErrors" -gt 5 ]]; then emailRecipient="$stakeholder" emailSubject="WARNING: ${log} showing unusual levels of errors" emailBody="${numErrors} errors found in log ${log}" echo "$emailBody" | mailx -s "$emailSubject" "$emailRecipient" fi done
Permintaan API
Misalkan Anda ingin mengumpulkan informasi tentang pengguna mana yang mengomentari kiriman Anda di Medium. Karena kami tidak memiliki akses langsung ke database situs ini, kami tidak akan membahas pertanyaan SQL. Namun, Anda dapat menggunakan berbagai API untuk mengakses data jenis ini.
Untuk menghindari percakapan panjang tentang otentikasi dan token, kami akan, sebagai titik akhir, menggunakan layanan pengujian API publik
JSONPlaceholder . Setelah menerima publikasi dari layanan dan mengekstraksi data dari kodenya di alamat email komentator, kami dapat memasukkan data ini ke dalam array:
endpoint="https://jsonplaceholder.typicode.com/comments" allEmails=() # 10 for postId in {1..10}; do # API response=$(curl "${endpoint}?postId=${postId}") # jq JSON allEmails+=( $( jq '.[].email' <<< "$response" ) ) done
Harap dicatat bahwa alat
jq digunakan di sini, yang memungkinkan penguraian JSON pada baris perintah. Kami tidak akan membahas detail bekerja dengan jq jika Anda tertarik dengan alat ini - lihat dokumentasi untuknya.
Bash atau Python?
Array - fitur yang berguna dan tersedia tidak hanya di bash. Orang yang menulis skrip untuk baris perintah mungkin memiliki pertanyaan logis tentang situasi mana yang layak digunakan bash, dan di mana, misalnya, Python.
Menurut pendapat saya, jawaban atas pertanyaan ini terletak pada seberapa banyak programmer tergantung pada teknologi tertentu. Katakanlah, jika masalah dapat diselesaikan langsung pada baris perintah, maka tidak ada yang mencegah penggunaan bash. Namun, jika skrip yang Anda minati adalah bagian dari proyek yang ditulis dengan Python, Anda dapat menggunakan Python.
Misalnya, untuk menyelesaikan masalah yang dipertimbangkan di sini, Anda dapat menggunakan skrip yang ditulis dengan Python, namun, ini akan menjadi penulisan pembungkus untuk Python untuk bash:
import subprocess all_threads = [1, 2, 4, 8, 16, 32, 64, 128] all_runtimes = [] # for t in all_threads: cmd = './pipeline --threads {}'.format(t) # subprocess , p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True) output = p.communicate()[0] all_runtimes.append(output)
Mungkin solusi untuk masalah ini dengan bash, tanpa melibatkan teknologi lain, lebih pendek dan lebih mudah dipahami, dan di sini Anda dapat melakukannya tanpa Python.
Ringkasan
Dalam materi ini kami menganalisis banyak desain yang digunakan untuk bekerja dengan array. Berikut adalah tabel tempat Anda akan menemukan apa yang telah kami ulas dan sesuatu yang baru.
Membangun sintaks | Deskripsi |
arr=() | Buat array kosong |
arr=(1 2 3) | Inisialisasi array |
${arr[2]} | Mendapatkan elemen array ketiga |
${arr[@]} | Mendapatkan semua elemen array |
${!arr[@]} | Mendapatkan indeks array |
${#arr[@]} | Perhitungan ukuran array |
arr[0]=3 | Menimpa elemen pertama array |
arr+=(4) | Bergabung dengan array nilai |
str=$(ls) | Menyimpan output ls sebagai string |
arr=( $(ls) ) | Menyimpan output dari ls sebagai array nama file |
${arr[@]:s:n} | Mendapatkan elemen array dari elemen dengan indeks s ke elemen dengan indeks s+(n-1)
|
Pada pandangan pertama, bash arrays mungkin tampak agak aneh, tetapi kemungkinan yang mereka berikan layak untuk mengatasi keanehan ini. Kami percaya bahwa setelah menguasai array bash, Anda akan sering menggunakannya. Sangat mudah untuk membayangkan skenario yang tak terhitung jumlahnya di mana array ini dapat berguna.
Pembaca yang budiman! Jika Anda memiliki contoh menarik menggunakan array dalam skrip bash, silakan bagikan.
