Dunia software modern bertukar data dalam format JSON: respons API, file konfigurasi, output docker inspect, hingga log terstruktur. Sayangnya, alat klasik Unix seperti grep dan awk dirancang untuk teks baris-kolom dan sering kebingungan menghadapi JSON yang bersarang. Di sinilah jq masuk: sebuah bahasa filter lengkap yang membaca JSON, memilih bagiannya, dan mencetaknya kembali dalam bentuk yang kita inginkan. Pada Chapter ini kita akan memakai jq untuk tugas yang paling sering muncul sehari-hari: membaca field, memfilter elemen, sampai menyusun output baru.

Contoh Data #

Sepanjang chapter kita memakai satu file data yang sama — daftar karyawan dalam format JSON. Simpan file berikut sebagai karyawan.json:

bash
cat > karyawan.json << 'EOF'
[
  {"nama": "Budi", "kota": "Bandung", "umur": 28},
  {"nama": "Siti", "kota": "Jakarta", "umur": 34},
  {"nama": "Andi", "kota": "Surabaya", "umur": 22}
]
EOF
jq '.' karyawan.json | head -5

Output yang Dihasilkan:

text
[
  {
    "nama": "Budi",
    "kota": "Bandung",
    "umur": 28

Mari kita telaah blok di atas:

  1. Baris 1-7 (cat > karyawan.json << 'EOF'): heredoc menulis tiga objek JSON ke dalam satu array — struktur paling umum dari respons API.
  2. Baris 8 (jq '.' karyawan.json): filter paling sederhana, identitas ., mem-parse input lalu mencetaknya kembali dengan indentasi rapi.
  3. (| head -5): membatasi tampilan lima baris pertama karena output pretty-print yang lengkap cukup panjang untuk layar.

Perhatikan beda penting dibanding cat: jq '.' tidak sekadar menyalin teks, melainkan mem-parse lalu merender ulang. File JSON yang indentasinya berantakan akan keluar rapi, dan file yang rusak akan ditolak dengan pesan galat parse — jadi jq '.' juga berguna sebagai pemeriksa validitas JSON tercepat.

Mengambil Field #

Akses field memakai notasi titik yang mengikuti struktur data. Field di akhir path dicetak sebagai JSON; menambahkan option -r (raw output) mencetak string polos tanpa tanda kutip sehingga siap dipakai perintah lain:

bash
echo '{"app": "stok", "versi": {"nomor": "2.1", "stabil": true}}' > info.json
jq '.app' info.json
jq -r '.app' info.json
jq -r '.versi.nomor' info.json

Output yang Dihasilkan:

text
"stok"
stok
2.1

Baris pertama mencetak "stok" lengkap dengan kutip JSON-nya, baris kedua mencetak stok tanpa kutip, dan baris ketiga menunjukkan navigasi field bersarang dengan menumpuk dua level titik. Pola jq -r '.field' file inilah yang paling sering muncul di skrip: nilai tunggal diekstrak, lalu disimpan ke variabel atau diteruskan ke perintah berikutnya.

Untuk array, notasi .[N] mengambil elemen ke-N (indeks mulai dari nol) dan .[] mengiterasi seluruh elemen:

bash
printf '[{"nama":"Budi","kota":"Bandung","umur":28},{"nama":"Siti","kota":"Jakarta","umur":34},{"nama":"Andi","kota":"Surabaya","umur":22}]' > karyawan.json
jq '.[1].nama' karyawan.json
jq -r '.[].kota' karyawan.json

Output yang Dihasilkan:

text
"Siti"
Bandung
Jakarta
Surabaya

Filter .[1].nama mengambil objek kedua lalu field namanya, sedangkan .[].kota menjalankan filter .kota terhadap setiap elemen array — output satu nilai per baris. Kombinasi iterasi dan seleksi field seperti ini adalah pola kerja 80% dari pemakaian jq sehari-hari.

Memfilter dengan select #

Kekuatan sesungguhnya muncul saat kita menyaring elemen berdasarkan isinya. Fungsi select(kondisi) meloloskan elemen yang kondisinya benar, dan operator pipe | merangkai filter layaknya pipe shell:

bash
printf '[{"nama":"Budi","kota":"Bandung","umur":28},{"nama":"Siti","kota":"Jakarta","umur":34},{"nama":"Andi","kota":"Surabaya","umur":22}]' > karyawan.json
jq '.[] | select(.umur > 25) | .nama' karyawan.json
jq '[.[] | select(.kota == "Jakarta")] | length' karyawan.json

Output yang Dihasilkan:

text
"Budi"
"Siti"
1

Mari kita bedah filter pertama langkah demi langkah: .[] mengeluarkan tiga objek karyawan satu per satu; select(.umur > 25) hanya meloloskan yang berumur di atas 25; | .nama mengambil field nama dari yang lolos. Filter kedua menunjukkan agregasi: kurung siku [...] mengumpulkan hasil kembali menjadi array, lalu length menghitungnya. Operator pembanding yang tersedia meliputi ==, !=, >, <, >=, <=, serta operasi boolean and, or, not.

Transformasi dan Output Baru #

Selain memilih, jq juga mampu merakit struktur output sendiri. Kurung kurawal di dalam filter membangun objek baru, dan fungsi keys serta length menyediakan informasi meta tentang data:

bash
printf '[{"nama":"Budi","kota":"Bandung","umur":28},{"nama":"Siti","kota":"Jakarta","umur":34},{"nama":"Andi","kota":"Surabaya","umur":22}]' > karyawan.json
jq -r '.[] | "\(.nama) dari \(.kota)"' karyawan.json
jq '.[0] | keys' karyawan.json

Output yang Dihasilkan:

text
Budi dari Bandung
Siti dari Jakarta
Andi dari Surabaya
[
  "kota",
  "nama",
  "umur"
]

Interpolasi "\(.nama)" di dalam string menyisipkan nilai field ke teks — pola yang menghasilkan laporan satu baris per entri, siap dipakai shell. Sementara keys mencantumkan seluruh nama field sebuah objek, berguna saat kita baru pertama kali membedah struktur JSON tak dikenal. Nilai dari variabel shell eksternal masuk lewat --arg nama nilai lalu dipakai sebagai $nama di dalam filter, menjaga tanda kutip tetap aman tanpa rentan galat penyisipan.

Sebagai penutup, berikut padanan tugas klasik Unix di dunia JSON:

TugasUnix klasikPadanan jq
Baca filecatjq '.'
Ambil kolomcut -d: -f2jq -r '.[].kota'
Filter barisgrep Bandungjq '.[] | select(.kota == "Bandung")'
Hitungwc -ljq 'length'
Urutkansortjq 'sort_by(.umur)'

Dengan jq di tangan, perpindahan antara dunia teks Unix klasik dan dunia JSON modern menjadi mulus: respons API bisa disaring di pipeline yang sama dengan grep dan awk. Kemampuan ini menuntup Section Text Processing — dan membuka Section System Monitoring, tempat kita belajar membaca denyut nadi sistem yang berjalan.