Skip to content

Pengelolaan Data di Command Line - seri Missing Semester

Updated: at 09.30

Pernahkah kamu ingin mendapatkan data dalam satu format dan mengubahnya menjadi format yang berbeda? Tentu saja pernah, ya kan! Secara umum, itulah yang akan kita bahas dalam artikel ini. Secara spesifik, kita akan membahas bagaimana memanipulasi data, baik dalam format teks maupun biner, hingga kita mendapatkan apa yang kita inginkan. Kita telah melihat beberapa pengelolaan data dasar di artikel-artikel sebelumnya. Hampir setiap kali kita menggunakan operator |, kita sedang melakukan semacam pengelolaan data. Pertimbangkan perintah seperti journalctl | grep -i intel. Perintah ini mencari semua entri log sistem yang menyebutkan Intel (tidak peduli besar kecilnya huruf). Kita mungkin tidak menganggapnya sebagai pengelolaan data, tetapi perintah tersebut mengubah dari satu format (seluruh log sistem) ke format yang lebih berguna bagi kita (hanya entri log Intel).

Sebagian besar pengelolaan data adalah tentang mengetahui alat apa yang kita miliki, dan bagaimana menggabungkannya. Ini mirip dengan memasak. Kita mengambil bahan-bahan mentah (data) dan mengubahnya menjadi sesuatu yang baru dan bermanfaat (informasi yang berguna). Dan seperti memasak, semakin banyak alat dan teknik yang kita kuasai, semakin banyak wawasan yang dapat kita peroleh!

Mari kita mulai dari awal. Untuk mengelola data, kita membutuhkan dua hal: data untuk diolah, dan sesuatu untuk dilakukan dengannya. Log sering kali menjadi contoh penggunaan yang baik, karena kita sering ingin menyelidiki sesuatu tentangnya, dan membaca keseluruhan log tidak praktis. Mari kita cari tahu siapa yang mencoba masuk ke server dengan melihat log server:

ssh myserver journalctl

Itu terlalu banyak. Mari kita batasi hanya untuk hal-hal terkait ssh:

ssh myserver journalctl | grep sshd

Perhatikan bahwa kita menggunakan pipe untuk mengalirkan/piping file remote melalui grep di komputer lokal kita! ssh itu ajaib, dan kita akan membahasnya lebih lanjut di artikel berikutnya tentang lingkungan command-line.

Meski begitu, ini masih jauh lebih banyak dari yang kita inginkan. Dan cukup sulit dibaca. Mari kita perbaiki:

ssh myserver 'journalctl | grep sshd | grep "Disconnected from"' | less

Mengapa ada tanda kutip tambahan? Nah, log kita mungkin cukup besar, dan akan sia-sia untuk mengalirkan semuanya ke komputer kita dan kemudian melakukan pemfilteran. Sebaliknya, kita dapat melakukan pemfilteran di server remote, dan kemudian mengolah datanya secara lokal. less memberi kita “pager” yang memungkinkan kita menggulir ke atas dan ke bawah melalui output yang panjang.

Untuk menghemat lalu lintas tambahan saat kita men-debug command line, kita bahkan dapat memasukkan log yang telah difilter saat ini ke dalam file sehingga kita tidak perlu mengakses jaringan saat mengembangkan:

$ ssh myserver 'journalctl | grep sshd | grep "Disconnected from"' > ssh.log
$ less ssh.log

Masih ada banyak noise di sini. Ada banyak cara untuk menyingkirkannya, tapi mari kita lihat salah satu alat paling kuat dalam toolkit kita: sed.

sed adalah “stream editor” yang dibangun di atas editor ed lama. Di dalamnya, kita pada dasarnya memberikan perintah singkat untuk bagaimana memodifikasi file, daripada memanipulasi isinya secara langsung (meskipun kita juga dapat melakukannya). Ada banyak sekali perintah, tapi salah satu yang paling umum adalah s: substitusi. Misalnya, kita dapat menulis:

ssh myserver journalctl | grep sshd | grep "Disconnected from" | sed 's/.*Disconnected from //'

Yang baru saja kita tulis adalah regular expression sederhana; sebuah konstruksi hebat yang memungkinkan Kita mencocokkan teks dengan pola. Perintah s ditulis dalam bentuk: s/REGEX/SUBSTITUTION/, di mana REGEX adalah regular expression yang ingin Kita cari, dan SUBSTITUTION adalah teks yang ingin Kita gunakan untuk menggantikan teks yang cocok.

(Anda mungkin mengenali sintaks ini dari bagian “Search and replace” dalam catatan VIM tingkat lanjut Vim kita! Memang, Vim menggunakan sintaks untuk pencarian dan penggantian yang mirip dengan perintah substitusi sed. Mempelajari satu alat sering membantu Kita menjadi lebih mahir dengan alat lainnya.)

Regular expression

Regular expression cukup umum dan berguna sehingga ada baiknya meluangkan waktu untuk memahami cara kerjanya. Mari kita mulai dengan melihat yang kita gunakan di atas: /.*Disconnected from /. Regular expression biasanya (meskipun tidak selalu) dikelilingi oleh /. Sebagian besar karakter ASCII hanya membawa arti normalnya, tapi beberapa karakter memiliki perilaku pencocokan “khusus”. Karakter mana yang melakukan apa bervariasi agak berbeda antara implementasi regular expression yang berbeda, yang merupakan sumber frustrasi besar. Pola yang sangat umum adalah:

Regular expression sed agak aneh, dan akan mengharuskan Kita untuk menempatkan \ sebelum sebagian besar ini untuk memberi mereka arti khusus. Atau Kita dapat menggunakan -E.

Jadi, melihat kembali ke /.*Disconnected from /, kita melihat bahwa itu cocok dengan teks apa pun yang dimulai dengan sejumlah karakter apa pun, diikuti oleh string literal “Disconnected from “. Yang mana itu yang kita inginkan. Tapi hati-hati, regular expression itu rumit. Bagaimana jika seseorang mencoba masuk dengan nama pengguna “Disconnected from”? Kita akan memiliki:

Jan 17 03:13:00 thesquareplanet.com sshd[2631]: Disconnected from invalid user Disconnected from 46.97.239.16 port 55920 [preauth]

Apa yang akan kita dapatkan? Nah, * dan + secara default “serakah”. Mereka akan cocok dengan teks sebanyak yang mereka bisa. Jadi, dalam contoh di atas, kita akan berakhir dengan hanya

46.97.239.16 port 55920 [preauth]

Yang mungkin bukan yang kita inginkan. Dalam beberapa implementasi regular expression, Kita dapat hanya menambahkan ? pada * atau + untuk membuatnya tidak serakah, tapi sayangnya sed tidak mendukung itu. Kita bisa beralih ke mode command-line perl, yang mendukung konstruksi itu:

perl -pe 's/.*?Disconnected from //'

Kita akan tetap menggunakan sed untuk selebihnya, karena itu adalah alat yang jauh lebih umum untuk jenis pekerjaan ini. sed juga dapat melakukan hal-hal berguna lainnya seperti menyisipkan teks (dengan perintah i), secara eksplisit mencetak baris (dengan perintah p), memilih baris berdasarkan indeks, dan banyak hal lainnya. Tapi kita tidak akan terlalu membahasnya di sini. sed pada dasarnya adalah topik tersendiri, tapi seringkali ada alat yang lebih baik.

Oke, jadi kita juga memiliki akhiran yang ingin kita singkirkan. Bagaimana kita melakukannya? Agak sulit untuk hanya mencocokkan teks yang mengikuti nama pengguna, terutama jika nama pengguna dapat memiliki spasi dan sejenisnya! Yang perlu kita lakukan adalah mencocokkan seluruh baris:

 | sed -E 's/.*Disconnected from (invalid |authenticating )?user .* [^ ]+ port [0-9]+( \[preauth\])?$//'

Mari kita lihat apa yang sedang terjadi dengan debugger regex. Oke, jadi awalnya masih sama seperti sebelumnya. Kemudian, kita mencocokkan salah satu varian “user” (ada dua awalan dalam log). Lalu kita mencocokkan string karakter apa pun di mana nama pengguna berada. Lalu kita cocokkan pada kata tunggal apa pun ([^ ]+; urutan non-kosong karakter non-spasi apa pun). Kemudian kata “port” diikuti oleh urutan digit. Kemudian mungkin akhiran [preauth], dan kemudian akhir baris.

Perhatikan bahwa dengan teknik ini, nama pengguna “Disconnected from” tidak akan membingungkan kita lagi. Bisakah Kita melihat mengapa?

Ada satu masalah dengan ini meskipun, dan itu adalah seluruh log menjadi kosong. Kita ingin menyimpan nama pengguna setelah semua. Untuk ini, kita dapat menggunakan “capture group”. Setiap teks yang cocok dengan regex yang dikelilingi oleh tanda kurung disimpan dalam kelompok tangkapan bernomor. Ini tersedia dalam substitusi (dan dalam beberapa mesin, bahkan dalam pola itu sendiri!) sebagai \1, \2, \3, dst. Jadi:

 | sed -E 's/.*Disconnected from (invalid |authenticating )?user (.*) [^ ]+ port [0-9]+( \[preauth\])?$/\2/'

Seperti yang mungkin Kita bayangkan, Kita dapat membuat regular expression yang sangat rumit. Misalnya, berikut adalah artikel tentang bagaimana Kita mungkin cocok dengan alamat email. Itu tidak mudah. Dan ada banyak [diskusi] (https://stackoverflow.com/questions/201323/how-to-validate-an-email-address-using-a-regular-expression/1917982). Dan orang-orang telah menulis tes. Dan matriks tes. Kita bahkan dapat menulis regex untuk menentukan apakah suatu angka tertentu adalah bilangan prima.

Regular expression terkenal sulit untuk digunakan dengan benar, tetapi juga sangat berguna untuk dimiliki dalam kotak alat Anda!

Ini seperti mempelajari bahasa asing - awalnya terlihat menakutkan dan membingungkan, tapi semakin banyak Kita berlatih, semakin mudah dan berguna. Dan seperti belajar bahasa, ada banyak “dialek” regex yang berbeda (implementasi), jadi penting untuk menyadari perbedaan dan kesamaannya.

Kembali ke pengelolaan data

Oke, jadi sekarang kita memiliki

ssh myserver journalctl
 | grep sshd
 | grep "Disconnected from"
 | sed -E 's/.*Disconnected from (invalid |authenticating )?user (.*) [^ ]+ port [0-9]+( \[preauth\])?$/\2/'

sed dapat melakukan semua macam hal menarik lainnya, seperti menyisipkan teks (dengan perintah i), secara eksplisit mencetak baris (dengan perintah p), memilih baris berdasarkan indeks, dan banyak hal lainnya. Periksa man sed!

Bagaimanapun. Apa yang kita miliki sekarang memberi kita daftar semua nama pengguna yang telah mencoba masuk. Tapi ini cukup tidak membantu. Mari kita cari yang umum:

ssh myserver journalctl
 | grep sshd
 | grep "Disconnected from"
 | sed -E 's/.*Disconnected from (invalid |authenticating )?user (.*) [^ ]+ port [0-9]+( \[preauth\])?$/\2/'
 | sort | uniq -c

sort akan, yah, mengurutkan inputnya. uniq -c akan meruntuhkan baris berturut-turut yang sama menjadi satu baris, dengan awalan jumlah kemunculannya. Kita mungkin juga ingin mengurutkannya dan hanya menyimpan nama pengguna yang paling umum:

ssh myserver journalctl
 | grep sshd
 | grep "Disconnected from"
 | sed -E 's/.*Disconnected from (invalid |authenticating )?user (.*) [^ ]+ port [0-9]+( \[preauth\])?$/\2/'
 | sort | uniq -c
 | sort -nk1,1 | tail -n10

sort -n akan mengurutkan dalam urutan numerik (bukan leksikografis). -k1,1 berarti “urutkan hanya berdasarkan kolom pertama yang dipisahkan oleh spasi putih”. Bagian ,n mengatakan “urutkan sampai bidang ke-n”, di mana defaultnya adalah akhir baris. Dalam contoh khusus ini, mengurutkan berdasarkan seluruh baris tidak akan berpengaruh, tapi kita di sini untuk belajar!

Jika kita menginginkan yang paling tidak umum, kita bisa menggunakan head sebagai ganti tail. Ada juga sort -r, yang mengurutkan dalam urutan terbalik.

Ini seperti memiliki tumpukan kartu dengan nama-nama, dan Kita ingin menemukan nama mana yang paling sering atau paling jarang muncul. Pertama Kita mengurutkan kartu berdasarkan nama (sort), lalu Kita menghitung berapa kali setiap nama muncul (uniq -c), lalu Kita mengurutkan tumpukan berdasarkan jumlah (sort -n), dan akhirnya Kita mengambil yang paling atas atau paling bawah dari tumpukan (head/tail). Voila! Kita telah mengubah tumpukan besar kartu yang berantakan menjadi informasi yang berguna.

Oke, jadi itu cukup keren, tapi bagaimana jika kita ingin mengekstrak hanya nama penggunanya sebagai daftar yang dipisahkan koma, mungkin untuk file konfigurasi?

ssh myserver journalctl
 | grep sshd
 | grep "Disconnected from"
 | sed -E 's/.*Disconnected from (invalid |authenticating )?user (.*) [^ ]+ port [0-9]+( \[preauth\])?$/\2/'
 | sort | uniq -c
 | sort -nk1,1 | tail -n10
 | awk '{print $2}' | paste -sd,

Jika Kita menggunakan macOS: perhatikan bahwa perintah seperti yang ditunjukkan tidak akan berfungsi dengan paste BSD yang dikirimkan dengan macOS. Lihat latihan 4 dari kuliah alat shell untuk lebih lanjut tentang perbedaan antara BSD dan GNU coreutils dan instruksi tentang cara menginstal GNU coreutils di macOS.

Mari kita mulai dengan paste: itu memungkinkan Kita menggabungkan baris (-s) dengan pembatas karakter tunggal yang diberikan (-d; , dalam kasus ini). Tapi apa ini awk?

awk — editor lain

awk adalah bahasa pemrograman yang kebetulan sangat baik dalam memproses aliran teks. Ada banyak yang bisa dikatakan tentang awk jika Kita mempelajarinya dengan benar, tetapi seperti banyak hal lainnya di sini, kita hanya akan membahas dasarnya.

Pertama, apa yang dilakukan {print $2}? Nah, program awk mengambil bentuk pola opsional ditambah blok yang mengatakan apa yang harus dilakukan jika pola cocok dengan baris tertentu. Pola default (yang kita gunakan di atas) cocok dengan semua baris. Di dalam blok, $0 diatur ke isi baris seluruhnya, dan $1 sampai $n diatur ke field ke-n dari baris itu, saat dipisahkan oleh pemisah field awk (spasi putih secara default, ubah dengan -F). Dalam hal ini, kita mengatakan bahwa, untuk setiap baris, cetak isi field kedua, yang kebetulan adalah nama pengguna!

Ini seperti memiliki spreadsheet raksasa, di mana setiap baris adalah entri log, dan Kita mengatakan “untuk setiap baris, cetak kolom 2”. Awk adalah cara yang sangat kuat (meskipun agak kriptik) untuk memproses data tabular dalam skrip shell.

Mari kita lihat apakah kita bisa melakukan sesuatu yang lebih mewah. Mari kita hitung jumlah nama pengguna sekali pakai yang dimulai dengan c dan diakhiri dengan e:

 | awk '$1 == 1 && $2 ~ /^c[^ ]*e$/ { print $2 }' | wc -l

Ada banyak yang perlu dibongkar di sini. Pertama, perhatikan bahwa sekarang kita memiliki pola (bagian yang ada sebelum {...}). Pola tersebut mengatakan bahwa field pertama baris harus sama dengan 1 (itu adalah hitungan dari uniq -c), dan bahwa field kedua harus cocok dengan regular expression yang diberikan. Dan bloknya hanya mengatakan untuk mencetak nama pengguna. Kemudian kita menghitung jumlah baris dalam output dengan wc -l.

Ini lebih rumit, tapi juga lebih kuat. Kita melakukan penyaringan berdasarkan jumlah kemunculan nama pengguna (hanya yang muncul sekali), dan juga berdasarkan pola nama penggunanya (harus dimulai dengan c dan diakhiri dengan e). Jadi ini seperti mengatakan “temukan semua kartu di tumpukan yang merupakan satu-satunya dengan nama itu, dan nama itu mulai dengan C dan berakhir dengan E, lalu hitung berapa banyak kartu seperti itu.”

Namun, ingat awk adalah bahasa pemrograman?

BEGIN { rows = 0 }
$1 == 1 && $2 ~ /^c[^ ]*e$/ { rows += $1 }
END { print rows }

BEGIN adalah pola yang cocok dengan awal input (dan END cocok dengan akhirnya). Sekarang, blok per baris hanya menambahkan hitungan dari field pertama (meskipun itu akan selalu 1 dalam kasus ini), dan kemudian kita mencetaknya di akhir. Bahkan, kita bisa menyingkirkan grep dan sed seluruhnya, karena awk dapat melakukan semuanya, tapi kita akan menyerahkannya sebagai latihan untuk pembaca.

Ini menunjukkan betapa kuatnya awk - itu seperti memiliki seluruh bahasa pemrograman yang dirancang untuk memproses data dalam pipa. Kita dapat melakukan aritmatika, menetapkan variabel, menggunakan pernyataan bersyarat, dan sebagainya. Jika Kita menemukan diri Kita menulis skrip shell yang sangat panjang dan rumit untuk memproses data teks, pertimbangkan untuk menulis ulang dalam awk - itu mungkin jauh lebih sederhana dan lebih kuat.

Menganalisis Data

Anda dapat melakukan matematika langsung di shell Kita menggunakan bc, kalkulator yang dapat membaca dari STDIN! Misalnya, tambahkan angka pada setiap baris bersama-sama dengan menggabungkannya, dibatasi dengan +:

 | paste -sd+ | bc -l

Atau buat ekspresi yang lebih rumit:

echo "2*($(data | paste -sd+))" | bc -l

Ini seperti memiliki kalkulator terpasang langsung ke pipa data Anda. Kita dapat mengalirkan angka ke bc, melakukan operasi matematika, dan mendapatkan hasilnya kembali. Ini dapat menjadi blok bangunan yang kuat untuk analisis data pada baris perintah.

Anda bisa mendapatkan statistik dalam berbagai cara. st cukup rapi, tapi jika Kita sudah memiliki R:

ssh myserver journalctl
 | grep sshd
 | grep "Disconnected from"
 | sed -E 's/.*Disconnected from (invalid |authenticating )?user (.*) [^ ]+ port [0-9]+( \[preauth\])?$/\2/'
 | sort | uniq -c
 | awk '{print $1}' | R --no-echo -e 'x <- scan(file="stdin", quiet=TRUE); summary(x)'

R adalah bahasa pemrograman (aneh) lainnya yang sangat bagus untuk analisis data dan plotting. Kami tidak akan membahas terlalu detail, tapi cukup untuk mengatakan bahwa summary mencetak statistik ringkasan untuk vektor, dan kami membuat vektor yang berisi aliran input angka, sehingga R memberi kami statistik yang kami inginkan!

Ini seperti memiliki seorang analis data yang duduk dalam pipa Anda. Kita mengalirkan beberapa angka, dan mereka memberi Kita kembali ringkasan statistik yang berguna. Dan jika Kita butuh sesuatu yang lebih khusus, Kita dapat memberi tahu mereka dengan tepat apa yang harus dilakukan dalam bahasa R. Sangat kuat untuk pekerjaan analisis data satu-off.

Jika Kita hanya menginginkan beberapa plot sederhana, gnuplot adalah teman Anda:

ssh myserver journalctl
 | grep sshd
 | grep "Disconnected from"
 | sed -E 's/.*Disconnected from (invalid |authenticating )?user (.*) [^ ]+ port [0-9]+( \[preauth\])?$/\2/'
 | sort | uniq -c
 | sort -nk1,1 | tail -n10
 | gnuplot -p -e 'set boxwidth 0.5; plot "-" using 1:xtic(2) with boxes'

Ini seperti memiliki plotter grafik di ujung pipa Anda. Kita memasukkan data, lalu gambar yang berguna muncul. Tidak terlalu fleksibel dibandingkan dengan, katakanlah, R atau Python dengan matplotlib, tapi untuk visualisasi cepat dari beberapa angka, itu sulit dikalahkan.

Wrangling Data untuk Membuat Argumen

Terkadang kita ingin melakukan wrangling data untuk menemukan hal-hal yang akan diinstal atau dihapus berdasarkan daftar yang lebih panjang. Kombinasi teknik wrangling data yang telah dibahas sejauh ini dengan xargs dapat menjadi duet yang kuat.

Contohnya, seperti yang diperlihatkan dalam kuliah, kita dapat menggunakan perintah berikut untuk menghapus instalan build malam Rust lama dari sistem dengan mengekstrak nama build lama menggunakan alat wrangling data dan kemudian meneruskannya melalui xargs ke uninstaller:

rustup toolchain list | grep nightly | grep -vE "nightly-x86" | sed 's/-x86.*//' | xargs rustup toolchain uninstall

Ini ibarat memiliki tim penghancur kecil dalam pipeline Anda. Kita menentukan apa yang perlu dihancurkan (misalnya, “semua bangunan lama di jalan ini yang namanya mengandung kata ‘berbahaya’”), dan mereka akan melakukannya untuk Anda, tanpa perlu mengetik setiap perintah penghancuran secara manual.

Mengelola Data Biner

Sejauh ini, kita sebagian besar membahas tentang pengelolaan data teks, tetapi pipeline sama bergunanya untuk data biner. Sebagai contoh, kita dapat menggunakan ffmpeg untuk menangkap gambar dari kamera, mengubahnya menjadi grayscale, mengompresnya, mengirimnya ke mesin jarak jauh melalui SSH, mendekompresinya di sana, membuat salinan, lalu menampilkannya.

ffmpeg -loglevel panic -i /dev/video0 -frames 1 -f image2 -
 | convert - -colorspace gray -
 | gzip
 | ssh mymachine 'gzip -d | tee copy.jpg | env DISPLAY=:0 feh -'

Ini seperti memiliki studio pengolahan gambar mini dalam pipeline. Ambil input biner (gambar dari kamera), manipulasi dengan berbagai cara (skala abu-abu, kompresi), kirim melalui internet (ssh), dan akhirnya tampilkan hasilnya, semuanya tanpa menyentuh disk! Pipeline tidak hanya untuk teks - mereka dengan senang hati menangani aliran byte, apa pun isinya.

Latihan

Artikel ini diakhiri dengan beberapa latihan untuk membantu Anda berlatih keterampilan pengelolaan data. Seperti set latihan dari artikel sebelumnya, anggap ini sebagai resep untuk dicoba - mereka memberikan bahan dan instruksi, tapi keputusan tepat cara memasaknya terserah Anda!

Misalnya, salah satu latihan meminta Anda mencari berapa kali sistem Anda melakukan boot dalam 10 boot terakhir. Ini seperti meminta Anda untuk menemukan berapa kali Anda pergi berlari dalam 10 entri terakhir buku harian kebugaran Anda. Anda memiliki data mentah (buku harian / log sistem), Anda tahu apa yang dicari (entri lari / pesan boot), tinggal menyaring kebisingan dan menghitung kemunculannya.

Latihan lainnya melibatkan pengunduhan dataset online dan mengekstraksi statistik darinya. Ini seperti mendapatkan buku resep baru dan diminta untuk menemukan resep dengan kalori terendah dan tertinggi. Pertama dapatkan data (unduh resep), lalu saring apa yang diperlukan (ekstrak kolom numerik), dan akhirnya ringkas dengan cara yang diminta (temukan min/maks).

Intinya, latihan-latihan ini dirancang untuk memberikan Anda “waktu bermain” dengan alat yang telah dibahas, dalam konteks masalah dunia nyata. Semakin sering Anda berlatih menggunakannya, semakin kuat sihir shell yang dapat Anda lakukan!

Kesimpulan

Dalam artikel ini, kita telah mempelajari lebih lanjut tentang seni manipulasi data di command line. Kita melihat bagaimana alat-alat canggih seperti sed, awk, dan xargs dapat digunakan untuk mengubah, menyaring, dan meringkas data teks dan biner dengan berbagai cara yang kuat. Kita juga menyinggung bagaimana shell dapat digunakan untuk matematika dan analisis data sederhana.

Sepanjang pembahasan, kita menggunakan analogi untuk menghubungkan konsep abstrak ini dengan kegiatan dan objek sehari-hari - memasak makanan, menyortir kartu, meminta bantuan pada asisten dan ahli. Tujuannya adalah membantu membangun intuisi tentang apa yang dilakukan alat-alat ini, meskipun detail teknisnya mungkin pada awalnya terasa asing.

Dengan mengakhiri menggunakan beberapa latihan praktis, artikel ini mendorong Anda untuk langsung mencoba dan merasakan sendiri keajaiban pengelolaan data ini. Karena sama seperti keterampilan lainnya, cara terbaik untuk menguasainya adalah dengan praktik langsung.

Latihan, Praktik, Berani Kotor itu baik. SEMANGAT Pokoke

Semoga artikel ini bermanfaat dan menginspirasi untuk ngulik shell lebih lanjut. Sampai jumpa di artikel berikutnya!