Chapter ss dan netstat menutup dengan janji dua alat klasik, dan inilah keduanya. curl dan wget adalah klien yang paling sering kita ketik begitu pindah kursi dari pihak yang mendengarkan ke pihak yang menyambung: yang pertama serba bisa mengirim dan menerima data lewat hampir semua protokol berbasis URL, yang kedua spesialis mengunduh berkas sampai tuntas. Keduanya tampak serupa di permukaan, tetapi dirancang untuk kebiasaan yang berbeda — curl untuk percakapan singkat request-response, wget untuk pengambilan berkas yang panjang dan sabar. Kita akan membedah keduanya dengan server lokal di loopback seperti biasa, lalu menutup dengan pemanggilan API JSON — pola yang sama dengan yang dipakai skrip otomatisasi setiap hari.

Dua Alat dengan Dua Kebiasaan #

Perbedaan paling dalam antara keduanya ada pada pertanyaan yang mereka jawab. curl menjawab “apa yang dikatakan server bila saya kirim permintaan ini?” — karenanya keluaran bawaannya adalah stdout, tempat hasil di-pipe ke perintah lain. wget menjawab “bagaimana mengambil berkas itu sampai tuntas?” — karenanya keluaran bawaannya adalah berkas di disk, lengkap dengan progress bar dan kemampuan melanjutkan unduhan yang terputus. Perbandingan singkatnya:

Aspekcurlwget
Fokus utamatransfer data dua arah (request/response)unduhan berkas
ProtokolHTTP(S), FTP, SFTP, SMTP, dan lainnyaHTTP(S), FTP
Keluaran bawaanmencetak ke stdoutmenyimpan ke berkas
Melanjutkan unduhan putus-C --c
Unduhan rekursiftidak didukung-r, --mirror

Mengunduh Berkas dengan curl #

Empat opsi curl menutup hampir semua kebutuhan harian. Opsi -s (silent) meredam progress bar agar keluaran bersih untuk di-pipe; -o nama.txt menyimpan balasan ke berkas bernama nama.txt; -O (huruf besar) menyimpan memakai nama berkas dari URL itu sendiri; dan -L mengikuti redirect HTTP — penting karena banyak URL modern hanya menjawab “pindah ke sana”. Kita uji rangkaian itu terhadap server mini di loopback, lalu memastikan berkas hasil unduhan identik dengan aslinya lewat diff:

bash
printf 'isi berkas penting\n' > berkas.txt
python3 -m http.server 8765 --bind 127.0.0.1 >/dev/null 2>&1 &
server=$!
for i in 1 2 3 4 5; do curl -s -o /dev/null 127.0.0.1:8765/ && break; sleep 0.2; done
curl -s 127.0.0.1:8765/berkas.txt
curl -s -o salinan.txt 127.0.0.1:8765/berkas.txt
diff berkas.txt salinan.txt && echo "berkas identik"
kill "$server"

Output yang Dihasilkan:

text
isi berkas penting
berkas identik

Mari kita bedah baris demi baris:

  1. Baris 1 (printf 'isi berkas penting\n' > berkas.txt): menyiapkan berkas asal yang isinya pasti — nanti dibandingkan dengan hasil unduhan.
  2. Baris 2-3: server HTTP Python dinyalakan di background pada 127.0.0.1:8765, PID-nya disimpan ke variabel server agar bisa dimatikan dengan bersih.
  3. Baris 4 (for i in ...): menunggu server siap — curl dicoba berulang sampai koneksi diterima, lalu break.
  4. Baris 5 (curl -s 127.0.0.1:8765/berkas.txt): mengambil berkas dan mencetak isinya ke stdout — perilaku bawaan curl tanpa -o.
  5. Baris 6 (curl -s -o salinan.txt ...): mengambil berkas yang sama, kali ini disimpan ke salinan.txt.
  6. Baris 7 (diff ... && echo ...): diff membandingkan dua berkas; bila identik ia diam, lalu echo menyatakan keberhasilan.
  7. Baris 8 (kill "$server"): mematikan server — kebiasaan penutup yang selalu kita ulang.

Membaca Kode Keluar curl #

curl melaporkan kegagalan lewat kode keluar (exit code), dan opsi -s meredam pesan kesalahan — bukan kegagalannya. Karena itu skrip yang memakai curl selalu memeriksa $?. Kode-kode yang paling sering membedah arah masalah:

KodeArtiPertanyaan yang terjawab
6nama host tidak dapat diterjemahkanDNS bermasalah?
7koneksi gagal atau ditolakhost atau port tidak menjawab?
22HTTP error (4xx/5xx) dengan opsi -fserver hidup tapi menolak?
28tenggat waktu habisjalur macet atau firewall membuang paket?

Tanpa -f, curl menganggap sukses setiap balasan HTTP — termasuk 404 — karena dari sisi transfer, datanya memang terkirim; -f membuat kode 404 dan kawan-kawannya diterjemahkan menjadi kegagalan kode 22. Berikut gejala koneksi ke port yang tidak didengarkan siapa pun, persis pemeriksaan penutup Chapter Network Diagnostics :

bash
curl -sS --max-time 2 127.0.0.1:9999/ 2>&1

Output yang Dihasilkan:

text
curl: (7) Failed to connect to 127.0.0.1 port 9999 after 0 ms: Could not connect to server

Opsi -sS meredam progress bar (-s) tetapi tetap menampilkan pesan kesalahan (-S), dan 2>&1 mengarahkan pesan itu ke stdout — kombinasi favorit di dalam skrip. Pesan Failed to connect dengan kode 7 berarti paket sampai ke mesin tetapi ditolak: tidak ada yang mendengarkan di port tersebut. Bila gejalanya justru menunggu lama lalu gagal kode 28, paket dibuang diam-diam di jalur — biasanya firewall; perbedaan dua gejala ini kita pakai lagi di Chapter Troubleshooting Network .

wget, Spesialis Unduhan #

wget mengambil satu URL dan menyimpannya sebagai berkas dengan nama yang sama — tanpa opsi apa pun. Di atas kebiasaan dasar itu, opsi yang sering dipakai: -c melanjutkan unduhan yang terputus dari byte terakhir, --limit-rate 500k membatasi kecepatan agar tidak melahap bandwidth kantor, -r mengunduh rekursif mengikuti tautan, dan --mirror menggabungkan rekursif dengan pengecekan stempel waktu untuk mereplikasi situs. Karena progress bar wget menampilkan angka kecepatan yang terus berubah, kita memakai -q agar keluarannya deterministik:

bash
printf 'isi berkas data\n' > data.txt
python3 -m http.server 8765 --bind 127.0.0.1 >/dev/null 2>&1 &
server=$!
for i in 1 2 3 4 5; do curl -s -o /dev/null 127.0.0.1:8765/ && break; sleep 0.2; done
wget -qO hasil.txt 127.0.0.1:8765/data.txt
cat hasil.txt
kill "$server"

Output yang Dihasilkan:

text
isi berkas data

Di sini -O hasil.txt memindahkan tujuan simpan dari data.txt menjadi hasil.txt, lalu cat membuktikan isinya utuh. Perhatikan bahwa blok ini memakai curl untuk menunggu server siap — dua alat ini memang saling melengkapi, bukan saling menggantikan.

Memanggil API dengan curl #

Separuh kegunaan curl yang tidak dimiliki wget ada di sini: mengirim permintaan HTTP dengan metode dan header pilihan kita. Opsi -X POST memilih metode, -H 'Content-Type: application/json' menambahkan header, dan -d '...' mengirim data di badan permintaan — --data @pesanan.json membacanya dari berkas, satu-satunya cara yang nyaman saat datanya panjang. Kita latihan dengan API kecil di loopback yang menerima JSON lalu menjawab JSON; servernya kita tulis dengan Python standar:

bash
printf '{"nama": "Budi", "item": ["kopi", "teh", "susu"]}' > pesanan.json
python3 -c '
import json
from http.server import BaseHTTPRequestHandler, HTTPServer

class Handler(BaseHTTPRequestHandler):
    def do_POST(self):
        panjang = int(self.headers.get("Content-Length", 0))
        data = json.loads(self.rfile.read(panjang))
        balasan = {"nama": data["nama"], "jumlah": len(data["item"])}
        isi = json.dumps(balasan).encode()
        self.send_response(200)
        self.send_header("Content-Type", "application/json")
        self.send_header("Content-Length", str(len(isi)))
        self.end_headers()
        self.wfile.write(isi)

    def log_message(self, *args):
        pass

HTTPServer(("127.0.0.1", 8765), Handler).serve_forever()
' >/dev/null 2>&1 &
server=$!
for i in 1 2 3 4 5; do curl -s -o /dev/null 127.0.0.1:8765/ && break; sleep 0.2; done
curl -s -X POST -H 'Content-Type: application/json' --data @pesanan.json 127.0.0.1:8765/pesanan
printf '\n'
curl -s -X POST -H 'Content-Type: application/json' --data @pesanan.json 127.0.0.1:8765/pesanan | jq -r '.nama + " memesan " + (.jumlah | tostring) + " item"'
kill "$server"

Output yang Dihasilkan:

text
{"nama": "Budi", "jumlah": 3}
Budi memesan 3 item

Mari kita bedah bagian-bagian pentingnya:

  1. Baris 1: berkas pesanan.json berisi data yang akan dikirim sebagai badan permintaan.
  2. Baris 2-24 (server API mini): handler membaca panjang badan dari header Content-Length, menguraikan JSON masukan, lalu menyusun balasan {"nama": ..., "jumlah": ...} — jumlah dihitung dari panjang array item.
  3. Baris 26-27: server dinyalakan di background dan ditunggu siap dengan pola loop yang sama.
  4. Baris 28-29: pemanggilan pertama mengirim pesanan.json dengan --data @pesanan.json lalu mencetak balasan mentahnya; printf '\n' hanya merapikan baris baru.
  5. Baris 30: pemanggilan kedua di-pipe ke jq -r — filter .nama dan .jumlah digabung menjadi satu kalimat, cara khas memproses jawaban API di dalam skrip.

Dengan curl dan wget di tangan, mesin kita bisa berbicara dengan layanan apa pun di jaringan. Akan tetapi keduanya hanya memindahkan data — login ke mesin jauh, menjalankan perintah di sana, dan menyalin berkas lewat koneksi terenkripsi adalah pekerjaan keluarga OpenSSH: ssh dan scp, Chapter berikutnya.

Referensi lanjut: halaman manual curl(1) untuk daftar lengkap opsi dan kode keluar, wget(1) untuk opsi rekursif dan resume, serta manual jq untuk bahasa filter JSON-nya.