Kuasai text processing di BASH: sed untuk find & replace dengan capture groups dan address ranges, awk untuk ekstraksi serta agregasi kolom, hingga parsing CSV/TSV. Disertai praktik menganalisis access.log secara nyata dan jebakan umum yang sering dialami pemula.

Setelah di episode 21 sebelumnya kita membahas pembuatan CLI professional dengan getopts — bagaimana skrip kalian membaca argumen, mengelola opsi, dan menyajikan help yang rapi — pada episode kali ini kita bergeser dari input ke data. Pertanyaan yang akan kita jawab bukan lagi "bagaimana skrip menerima perintah", melainkan "bagaimana skrip mengolah teks, file, dan log dengan cepat dan andal".
Di server produksi, hampir semua data bukanlah database — melainkan teks. Log aplikasi, file konfigurasi, daftar pengguna dalam format CSV, output dari perintah lain, semuanya berwujud baris dan kolom yang harus dipotong, diubah, digabung, dan dihitung. BASH murni sebenarnya bisa melakukannya dengan loop for dan while, tapi seperti membongkar mesin mobil hanya dengan obeng: bisa, tetapi lambat, rentan bug, dan tidak elegan. Di sinilah sed dan awk hadir sebagai dua alat khusus yang mengerjakan pekerjaan itu dalam satu baris.
Anggap saja kalian adalah petugas arsip sebuah kantor. Sed adalah mesin potong dan tempel yang cerdas: bisa menemukan baris tertentu dan menggantinya tanpa membuka file di editor. Awk adalah kalkulator spreadsheet di terminal: bisa membaca tabel, memilih kolom, dan menjumlahkan nilainya dalam sekali jalan. Kombinasi keduanya — ditambah pipeline BASH — menjadikan skrip kalian mampu mengolah data berukuran ratusan ribu baris dalam hitungan detik, sesuatu yang mustahil dilakukan secara manual.
Pada episode ini kita akan membedah sed terlebih dahulu, lalu awk, kemudian parsing CSV/TSV di BASH murni, dan menutup dengan praktik nyata menganalisis access.log server web beserta jebakan umum yang paling sering menjebak.
Sebelum masuk ke sintaks, pahami mengapa keterampilan ini begitu berharga. Prinsipnya berasal dari Unix philosophy: setiap program melakukan satu hal dengan baik, dan pipeline menyatukannya. Daripada menulis 50 baris loop di BASH untuk memproses sebuah file, kalian cukup menyusun beberapa filter kecil yang saling melempar data lewat |.
Bayangkan skenario ini: ada 500 ribu baris di access.log dan seorang pimpinan bertanya, "siapa 10 alamat IP paling banyak mengunjungi situs kita pagi ini?". Menjawab dengan editor teks atau Excel membutuhkan waktu berjam-jam. Dengan awk, sort, dan uniq, jawabannya keluar dalam sepersekian detik. Kemampuan ini yang membedakan admin yang mengoperasikan server dari admin yang memahami server.
Ada tiga level pemrosesan teks yang akan kita bangun:
Masing-masing punya tempatnya. Kunci di episode ini adalah tahu kapan memakai yang mana.
Nama sed singkatan dari stream editor. Berbeda dengan vi atau nano yang menunggu interaksi manusia, sed menerima teks, memprosesnya baris demi baris sesuai perintah, lalu menampilkan hasilnya — sempurna untuk pipeline. Operasi yang paling sering dipakai adalah substitusi:
echo "hello world, hello bash" | sed 's/world/bash/'
# hello bash, hello bashSecara default, s/.../.../ hanya mengganti kemunculan pertama pada setiap baris. Tambahkan flag g (global) untuk mengganti semua:
echo "hello world, hello bash" | sed 's/hello/hai/g'
# hai world, hai bashHal yang membuat sed benar-benar berdaya adalah dukungan regex (ingat kembali pola regex dari episode 6) dan capture groups. Dengan opsi -E (extended regex), kita bisa menangkap sebagian pola dan menyusun ulang hasilnya menggunakan \1, \2, dan seterusnya:
echo "user=arman-putra" | sed -E 's/user=(.*)-(.*)/user=\1 \2/'
# user=arman putraPola (.*)-(.*) menangkap dua bagian sebelum dan sesudah tanda hubung, lalu \1 \2 menyusun ulang keduanya dengan spasi. Ini sangat berguna untuk normalisasi data: mengubah format tanggal, merapikan nama file, atau memisahkan kolom yang tergabung.
Untuk mengubah file secara langsung, gunakan -i (in-place). Contoh paling klasik di dunia DevOps adalah mengedit file konfigurasi dari dalam skrip:
sed -i 's/#Port 22/Port 2222/' /etc/ssh/sshd_config
sed -i 's/#PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_configBayangkan betapa berbahayanya tanpa sed: kalian harus membuka sshd_config, mencari baris, mengubahnya, menyimpan — semua secara manual di puluhan server. Dengan sed, satu skrip mengubah semuanya sekaligus, dan hasilnya konsisten.
Sed juga mendukung address ranges — memilih baris spesifik untuk diproses. Sintaksnya awal,akhir + perintah:
sed -n '2,4p' file.txt # cetak baris 2 sampai 4 saja
sed '/^#/d' file.conf # hapus semua baris komentar (diawali #)
sed '/^DEBUG/,/^END/p' log # cetak rentang antara DEBUG dan ENDOpsi -n menekan output default sehingga hanya baris yang diminta (p) yang dicetak — setara dengan "filter baris". Dan d menghapus baris yang cocok. Kombinasi -n + /patern/p ini adalah cara sed meniru fungsi grep dengan kontrol lebih besar.
Tip
Biasakan berlatih dengan pipa, bukan file asli. Sebelum menerapkan pola sed ke file produksi, uji pada contoh kecil di terminal: echo "contoh data" | sed 's/.../'. Hasil langsung terlihat tanpa risiko. Setelah pola terbukti, baru tambahkan -i untuk menulis perubahan ke file — kebiasaan kecil ini menyelamatkan banyak file konfigurasi dari pola regex yang salah.
Jika sed beroperasi pada baris, awk beroperasi pada kolom. Awk bekerja dengan model pattern { action }: untuk setiap baris yang cocok dengan pattern, jalankan action. Secara default awk memecah setiap baris menjadi kolom berdasarkan spasi atau tab, dan menyimpannya di variabel $1, $2, dan seterusnya. $0 adalah seluruh baris.
awk '{ print $1, $3 }' access.logBeberapa variabel bawaan yang wajib kalian hafal:
| Variabel | Arti |
|---|---|
$0 | Seluruh baris saat ini |
$1, $2, … | Kolom ke-1, ke-2, dan seterusnya |
NF | Number of Fields — jumlah kolom pada baris saat ini |
NR | Number of Records — nomor baris yang sedang diproses |
FS | Field Separator — pemisah kolom (default spasi/tab) |
BEGIN | Blok yang dijalankan sekali sebelum membaca baris pertama |
END | Blok yang dijalankan sekali setelah baris terakhir |
Kekuatan sebenarnya awk muncul di blok BEGIN dan END, yang memungkinkan agregasi. Contoh klasik: menjumlahkan ukuran respons (kolom 10) di access log — format log standar Apache/Nginx:
awk '{ total += $10 } END { print "Total bytes:", total }' access.logBaris total += $10 dijalankan untuk setiap baris; begitu selesai, END mencetak hasil akhirnya. Inilah "kalkulator spreadsheet" yang tadi disebutkan — dan ia berjalan untuk jutaan baris tanpa kehabisan memori, karena awk memproses satu baris pada satu waktu.
Awk juga bisa mencetak dengan format terkendali lewat printf, dan menangkap nilai di dalam skrip. Hasil eksekusi awk bisa ditangkap sebagai variabel BASH:
total_404=$(awk '$9 == 404 { c++ } END { print c+0 }' access.log)
echo "Jumlah 404: $total_404"Perhatikan ekspresi $9 == 404 — ia bertindak sebagai pattern: hanya baris dengan kolom ke-9 bernilai 404 yang diproses. Ini adalah cara awk melakukan filter dan hitung sekaligus, satu pipeline tanpa grep tambahan.
Tidak semua pemrosesan teks bisa ditangani sed/awk. Kadang kalian perlu membaca file CSV/TSV baris per baris dan menjalankan logika BASH untuk setiap record — misalnya memanggil API untuk setiap baris, atau membuat direktori untuk setiap pengguna. Untuk ini, BASH murni dengan read adalah jawabannya:
while IFS=',' read -r nama email role; do
echo "Membuat akun: $nama ($role)"
useradd "$nama" -m
done < users.csvAda tiga bagian penting yang sering disalahpahami:
IFS=',' — menetapkan koma sebagai pemisah kolom untuk read ini saja, tanpa mengubah IFS global skrip.-r — mencegah backslash di dalam file ditafsirkan sebagai escape; tanpa ini, path Windows seperti C:\data bisa berubah menjadi C:data.< users.csv — redirection file ke dalam loop; ini membuat loop berjalan dalam subshell-free konteks yang sama dengan skrip.Pola yang sama berlaku untuk TSV (tab-separated): cukup ganti IFS=',' dengan IFS=$'\t'.
Warning
Parsing CSV dengan BASH murni punya batas tegas: ia memecah baris berdasarkan karakter separator sederhana dan tidak memahami tanda kutip. Jika file kalian berisi nilai dengan koma di dalam tanda kutip (misal "Arman, S.Kom"), loop read akan salah memecah kolom. Untuk CSV yang benar-benar kompleks, gunakan awk dengan logika kutip, atau library khusus seperti csvkit — jangan memaksakan BASH murni di luar batas kemampuannya.
Saatnya menggabungkan semuanya. Anggap kita punya access.log dengan format standar:
203.0.113.10 - - [02/Aug/2026:08:15:22 +0000] "GET /products HTTP/1.1" 200 4321
198.51.100.4 - - [02/Aug/2026:08:15:31 +0000] "GET /products HTTP/1.1" 200 4321
192.0.2.77 - - [02/Aug/2026:08:16:05 +0000] "POST /api/orders HTTP/1.1" 500 214
203.0.113.10 - - [02/Aug/2026:08:16:44 +0000] "GET /assets/app.css HTTP/1.1" 200 83102Kolomnya: IP (1), ident (2), user (3), timestamp (4-5), request (6-8), status (9), bytes (10). Sekarang jawab empat pertanyaan bisnis sekaligus:
total_req=$(wc -l < access.log)
unique_ip=$(awk '{ print $1 }' access.log | sort -u | wc -l)
total_bytes=$(awk '{ s += $10 } END { print s }' access.log)
echo "Total request : $total_req"
echo "IP unik : $unique_ip"
echo "Total bytes : $total_bytes"
echo "Top 10 IP tersibuk:"
awk '{ print $1 }' access.log | sort | uniq -c | sort -rn | head -10Perhatikan dua pola penting:
awk '{print $1}' | sort | uniq -c — urutkan dulu, baru uniq. Berbeda dengan sort -u, uniq -c sekaligus memberikan jumlah kemunculan, sehingga kita bisa mengurutkan lagi dengan sort -rn untuk mendapatkan peringkat.wc -l < file menggunakan redirection, bukan argumen — ini mencegah nama file tercetak di output.Untuk 500 ribu baris, semua perintah di atas selesai dalam hitungan detik. Coba bayangkan mengerjakannya manual di editor teks.
Pertanyaan yang paling sering muncul: "kapan saya pakai yang mana?". Aturan praktisnya sederhana:
| Kebutuhan | Alat yang Tepat |
|---|---|
| Ganti teks di dalam file | sed -i |
| Ambil / hapus / cetak baris tertentu | sed dengan address range |
| Potong dan susun ulang kolom | awk |
| Jumlahkan / hitung agregasi | awk dengan BEGIN/END |
| Jalankan logika BASH untuk setiap record | loop while read |
| CSV dengan kutipan kompleks | library CSV (mis. csvkit) |
Mental modelnya: baris → sed, kolom → awk, logika → BASH murni. Begitu sebuah tugas melibatkan perulangan dengan kondisi dan pemanggilan perintah lain, pindahkan ke loop BASH. Begitu tugas hanya menyaring dan mengubah bentuk data, biarkan sed/awk yang menanganinya dalam satu baris.
1. sed -i yang tidak portabel. Di GNU sed (Linux) sed -i bekerja langsung. Di BSD sed (macOS) sed -i butuh argumen backup, sehingga sed -i 's/x/y/' error di Mac. Solusi yang portabel di keduanya: sed -i.bak 's/x/y/' — memberi backup sekaligus. Hapus file .bak jika tidak dibutuhkan.
2. Awk FS vs BASH IFS. Keduanya sama-sama "pemisah kolom", tapi hidup di dunia berbeda. Awk membaca FS (default spasi, diatur dengan -F atau variabel FS), sedangkan read di BASH membaca IFS. Menulis IFS=',' di dalam skrip tidak akan mengubah perilaku awk, dan -F, tidak mengubah read. Jangan tertukar konteks.
3. Baris CRLF yang diam-diam merusak kolom terakhir. File yang dibuat di Windows berakhir dengan \r\n. Saat di-parse, \r menempel di akhir kolom terakhir — contohnya nilai 4321 berubah menjadi 4321\r, dan penjumlahan awk bisa menghasilkan angka aneh. Bersihkan dulu di awal pipeline:
cat data.csv | while IFS=',' read -r a b c; do ...; done
sed 's/\r$//' data.csv | while IFS=',' read -r a b c; do ...; done4. Lupa bahwa uniq butuh input terurut. uniq hanya menghapus duplikat yang berdekatan. Menggunakan uniq tanpa sort menghasilkan hitungan yang salah. Selalu urutkan dulu, atau gunakan sort -u bila hanya butuh daftar unik tanpa hitungan.
5. Menguji sed langsung ke file produksi. Selalu uji di salinan dulu tanpa -i, atau gunakan sed -i.bak. Satu pola regex yang salah bisa menghancurkan file konfigurasi yang menjadi sandaran ratusan pengguna.
Pada episode 22 ini, kalian telah menguasai fondasi text processing di BASH. Kita mulai dari sed: substitusi dengan s/.../.../, capture groups dengan \1 untuk menyusun ulang pola, address ranges untuk memilih baris tertentu, hingga sed -i untuk mengedit file konfigurasi langsung dari skrip. Lalu awk: membaca kolom dengan $1, NF, dan NR, menjumlahkan agregasi di blok END, serta memfilter dengan ekspresi seperti $9 == 404. Kita juga belajar parsing CSV/TSV dengan loop while IFS=',' read -r, lalu mempraktikkan semuanya untuk menganalisis access.log — total request, IP unik, total byte, dan 10 IP tersibuk — dalam satu rangkaian pipeline.
Poin kunci yang perlu kalian bawa:
-E + \1) mengubah sed dari "pengganti teks" menjadi "pemroses struktur".BEGIN/END menangani jutaan baris tanpa boros memori.-r pada read, selalu sedar diri akan batas BASH murni untuk CSV kompleks.\r (CRLF) dan uji tanpa -i sebelum menyentuh file produksi.Kemampuan mengolah data dari file ini menjadi jembatan menuju dunia yang lebih luas: di episode 23 selanjutnya kita akan membahas interaksi dengan system tools & external APIs — menggunakan curl untuk memanggil HTTP API, jq untuk memproses JSON, mengirim notifikasi ke Slack dan Telegram, serta menjalankan query database non-interaktif. Pastikan semangat kalian menyala!