Pipe dan redirection

Model stream stdin, stdout, dan stderr beserta pipe dan redirection sebagai gagasan inti shell.

Daftar isi

Setiap perintah di terminal membaca data dan menghasilkan data melalui tiga saluran yang disebut stream, yaitu aliran masukan dan keluaran yang dilalui data selama proses berjalan. Topik ini membahas model tiga stream tersebut, redirection sebagai mekanisme pembelokan aliran data ke berkas atau dari berkas, dan pipe sebagai sambungan langsung antara dua perintah. Setelah topik ini dibaca, perintah-perintah kecil dapat digabungkan menjadi pipeline yang menyelesaikan pengolahan data dalam satu baris terminal.

Tiga stream standar#

Setiap proses yang dijalankan shell menerima tiga stream sejak proses dimulai. Standard input (stdin) adalah aliran masukan tempat proses membaca data, standard output (stdout) adalah aliran tempat proses menulis keluaran normalnya, sedangkan standard error (stderr) adalah aliran khusus untuk pesan error. Ketiganya secara default tersambung ke terminal, sehingga keluaran dan pesan error sama-sama tampil di layar. Yang membedakan stdout dan stderr adalah salurannya, bukan tempat tampilnya.

Shell menunjuk ketiga stream tersebut dengan bilangan yang disebut file descriptor, yaitu penanda berupa angka untuk setiap aliran data milik proses. Angka 0 menunjuk stdin, angka 1 menunjuk stdout, dan angka 2 menunjuk stderr. Pemisahan ini bukan detail teknis yang sepele. Karena salurannya terpisah, keluaran normal dapat disimpan ke berkas sementara pesan error tetap tampil di terminal, atau keduanya diarahkan ke tempat yang berbeda. Diagram berikut memperlihatkan dua bentuk aliran yang dibahas dalam topik ini, yaitu sambungan antarproses melalui pipe dan pembelokan keluaran ke berkas.

Aliran data antarproses melalui pipe dan pembelokan ke berkasstdinperintah pertamapipeperintah keduastdoutnilai.txtstdinperintahstdouthasil.txt
Aliran data antarproses melalui pipe dan pembelokan ke berkas melalui redirection

Contoh pada topik ini menggunakan berkas nilai.txt berisi lima baris nilai ujian. Data yang sama dipakai berulang pada setiap bagian di bawah. Berkas itu dibuat dengan perintah berikut.

bash
# membuat berkas contoh daftar nilai ujian
printf 'Ali 86\nBudi 78\nCitra 92\nDewi 81\nAli 90\n' > nilai.txt
cat nilai.txt
text Keluaran
Ali 86
Budi 78
Citra 92
Dewi 81
Ali 90

Perhatikan tanda > pada perintah pertama. Tanda itu sudah merupakan redirection, dan cara kerjanya dibahas pada bagian berikutnya.

Redirection keluaran ke berkas#

Redirection mengubah tujuan atau sumber sebuah stream. Operator > menyimpan stdout sebuah perintah ke dalam berkas. Apabila berkas tujuan belum ada, berkas itu dibuat, sedangkan apabila berkas sudah ada, isinya ditimpa dari awal. Operator >> bekerja dengan cara yang sama, tetapi menambahkan data di akhir berkas tanpa menghapus isi lama. Keduanya ditulis langsung di antara perintah dan nama berkas tanpa perintah tambahan.

bash
# menyimpan hasil sort ke berkas baru
sort nilai.txt > nilai-terurut.txt
# menambahkan satu baris baru di akhir berkas
printf 'Eka 88\n' >> nilai-terurut.txt
cat nilai-terurut.txt
text Keluaran
Ali 86
Ali 90
Budi 78
Citra 92
Dewi 81
Eka 88

Perintah sort mengurutkan baris menurut urutan byte karakter, sehingga Ali 86 muncul sebelum Ali 90 dan seluruh nama berawalan A berada sebelum Budi. Hal penting yang perlu diperhatikan adalah posisi keluarannya. Lima baris yang semula tampil di terminal kini berpindah ke nilai-terurut.txt, sedangkan terminal tidak menampilkan apa pun dari perintah sort itu sendiri.

Redirection masukan dan pesan error#

Operator < membalik arah redirection, yaitu menjadikan isi sebuah berkas sebagai stdin perintah. Bentuk wc -l < nilai.txt menghitung banyaknya baris di dalam berkas tanpa menyebut nama berkas sebagai argumen, karena wc membaca data langsung dari stdin.

bash
# menghitung baris dengan isi berkas sebagai stdin
wc -l < nilai.txt
text Keluaran
5

Perilaku berbeda muncul apabila perintah mengalami error. Operator > hanya membelokkan stdout, sedangkan pesan error mengalir di stderr yang masih tersambung ke terminal. Contoh berikut meminta ls menampilkan dua berkas sekaligus, yaitu satu berkas yang ada dan satu berkas yang tidak ada.

bash
# keluaran normal disimpan ke berkas, pesan error tetap tampil di terminal
ls nilai.txt tidak-ada.txt > daftar.txt
cat daftar.txt
text Keluaran
ls: cannot access 'tidak-ada.txt': No such file or directory
nilai.txt

Baris error tidak ikut masuk ke daftar.txt karena stderr tidak tersentuh operator >. Untuk mengatur stderr, stream dirujuk melalui nomor file descriptor-nya. Operator 2> menyimpan stderr ke berkas terpisah, sedangkan operator 2>&1 menyalin stderr ke tujuan yang saat itu ditunjuk stdout, sehingga keduanya menyatu di satu berkas.

bash
# menyatukan keluaran normal dan pesan error ke satu berkas
ls nilai.txt tidak-ada.txt > semua.txt 2>&1
cat semua.txt
text Keluaran
ls: cannot access 'tidak-ada.txt': No such file or directory
nilai.txt

Pipe menyambungkan dua perintah#

Pipe ditulis sebagai karakter | di antara dua perintah. Operator ini menyambungkan stdout perintah di sebelah kiri ke stdin perintah di sebelah kanan, sehingga data mengalir langsung dari satu proses ke proses berikutnya tanpa berkas perantara. Rangkaian perintah yang disambungkan dengan pipe disebut pipeline. Shell menjalankan seluruh perintah di dalam pipeline secara bersamaan, dan keluaran mengalir seketika dari satu tahap ke tahap berikutnya.

bash
# mengurutkan lalu mengambil tiga baris pertama
sort nilai.txt | head -3
text Keluaran
Ali 86
Ali 90
Budi 78

Tahap kiri mengurutkan seluruh berkas, sedangkan tahap kanan hanya menampilkan tiga baris pertamanya. Contoh berikut memakai grep, yaitu perintah yang mencetak setiap baris yang memuat pola tertentu, untuk memilih baris berisi nama Ali sebelum dihitung oleh wc.

bash
# menghitung banyaknya baris yang memuat nama Ali
grep Ali nilai.txt | wc -l
text Keluaran
2

Kedua pipeline di atas dapat ditulis tanpa pipe dengan menyimpan keluaran ke berkas perantara terlebih dahulu, tetapi bentuk pipe lebih ringkas dan tidak meninggalkan berkas sisa. Gabungan pendek seperti inilah bentuk paling umum dari one-liner, yaitu kebiasaan menyelesaikan pengolahan data dengan satu baris pipeline yang disusun dari perkakas kecil yang masing-masing memiliki satu tugas.

Menyalin di tengah pipeline dengan tee#

Terkadang keluaran sebuah tahap pipeline perlu disimpan sekaligus diteruskan ke tahap berikutnya. Perintah tee melakukan hal itu dengan membaca stdin, menyalinnya ke sebuah berkas, dan tetap mengirimkannya ke stdout.

bash
# menyimpan salinan di tengah pipeline sambil meneruskan keluaran
sort nilai.txt | tee salinan-terurut.txt | head -3
text Keluaran
Ali 86
Ali 90
Budi 78
bash
# berkas salinan memuat seluruh baris, bukan hanya yang ditampilkan head
wc -l salinan-terurut.txt
text Keluaran
5 salinan-terurut.txt

Keluaran yang tampil hanya tiga baris karena head memotongnya, sedangkan salinan-terurut.txt memuat kelima baris karena tee menulis salinan sebelum data tiba di tahap terakhir. Pola ini sering dipakai untuk mengamati isi tengah pipeline sambil tetap melihat hasil akhirnya. Penyimpanan salinan tidak memperlambat aliran data karena tee menulis dan meneruskan pada waktu yang sama.

Satu hal perlu diingat. Pipe hanya meneruskan stdout, sehingga pesan error dari tahap kiri tidak ikut mengalir ke tahap kanan. Contoh berikut mencoba menampilkan berkas yang tidak ada, dan wc hanya menerima masukan kosong.

bash
# pesan error tidak mengalir melalui pipe karena hanya stdout yang diteruskan
ls tidak-ada.txt | wc -l
text Keluaran
ls: cannot access 'tidak-ada.txt': No such file or directory
0

Pesan error tetap tampil karena dialirkan melalui stderr ke terminal, sedangkan wc menghitung nol baris dari masukan kosongnya. Apabila pesan error memang perlu ikut mengalir, stderr harus disatukan ke stdout terlebih dahulu dengan 2>&1 sebelum masuk ke pipe.

Kesalahan umum#

Operator 2>&1 ditulis sebelum redirection berkas. Operator 2>&1 menyalin stderr ke tujuan yang saat penulisan itu ditunjuk stdout. Apabila urutannya terbalik, stdout masih menunjuk terminal saat penyalinan terjadi, sehingga pesan error lolos ke layar.

bash
# urutan terbalik: pesan error tetap tampil di terminal
ls nilai.txt tidak-ada.txt 2>&1 > semua.txt
text Keluaran
ls: cannot access 'tidak-ada.txt': No such file or directory

Pada keluaran di atas, pesan error tidak ikut masuk ke semua.txt karena redirection > semua.txt baru berlaku setelah 2>&1 dieksekusi. Bentuk yang benar adalah > semua.txt 2>&1, sebagaimana dipakai pada bagian redirection masukan dan pesan error.

Menimpa berkas masukan dengan >. Redirection > menimpa berkas tujuan sebelum perintah berjalan, sehingga sort nilai.txt > nilai.txt menghapus data yang seharusnya dibaca. Perbaikannya adalah menyimpan ke berkas lain terlebih dahulu, lalu memindahkannya apabila hasil penggantian memang diinginkan.

Mengira semua keluaran otomatis masuk ke pipe atau berkas. Pipe hanya meneruskan stdout dan operator > hanya membelokkan stdout, sehingga pesan error tetap tampil di terminal dan tidak tersimpan. Apabila pesan error perlu ikut disimpan atau diteruskan, tambahkan 2>&1 pada posisi yang benar.

Ringkasan#

  • Setiap proses memiliki tiga stream, yaitu stdin untuk masukan, stdout untuk keluaran normal, dan stderr untuk pesan error.
  • Operator > dan >> menyimpan stdout ke berkas, 2> membelokkan stderr, dan 2>&1 menyatukan stderr ke tujuan stdout.
  • Pipe | menyambungkan stdout perintah kiri ke stdin perintah kanan dan hanya meneruskan stdout.
  • tee menyimpan salinan keluaran ke berkas tanpa memutus pipeline.

Referensi#