xargs dan paralelisme

Mengisi pipeline dengan xargs, opsi -P untuk paralelisme, dan pengenalan GNU parallel.

Daftar isi

Topik ini membahas xargs, yaitu perintah yang mengisi pipeline dengan argumen, opsi -P untuk menjalankan tugas secara paralel, serta pengenalan GNU parallel sebagai alternatif yang lebih kaya fitur. Alat-alat ini lazim dijumpai pada server, runner CI, dan container ketika banyak berkas harus diproses dalam satu langkah. Setelah membaca topik ini, keluaran suatu perintah dapat disalurkan kepada perintah apa pun, bukan hanya kepada perintah yang membaca masukan standar, dan pekerjaan berulang dapat dibagi ke beberapa proses sekaligus.

Cara kerja xargs#

Pipeline menyalurkan keluaran standar satu proses ke masukan standar proses berikutnya. Perintah seperti cat, sort, dan wc membaca masukan standar secara langsung, sehingga pipeline di antara perintah tersebut bergerak tanpa bantuan tambahan. Sebagian besar perintah lain tidak berperilaku demikian. Perintah cp, rm, chmod, dan kill hanya menerima argumen dari baris perintahnya, sehingga nama berkas yang disalurkan lewat pipe tidak dibaca sama sekali oleh perintah-perintah tersebut.

xargs menjembatani perbedaan ini. Ia membaca teks dari masukan standar, memecahnya menjadi argumen berdasarkan spasi dan pemisah baris, lalu menjalankan perintah lain dengan argumen tersebut diletakkan di belakang nama perintah. Apabila tidak ada perintah yang ditentukan, xargs menjalankan echo, sehingga perilaku bawaannya mudah diamati. Pada contoh berikut, wc -l menerima ketiga nama berkas melalui satu proses xargs, bukan melalui tiga proses terpisah.

bash
# membuat tiga berkas catatan sebagai bahan uji
printf 'belajar shell\npipeline dasar\n' > catatan-1.txt
printf 'variabel dan fungsi\n' > catatan-2.txt
printf 'redirection\nloop for\nfungsi\n' > catatan-3.txt
# menghitung jumlah baris setiap berkas dengan xargs
ls catatan-*.txt | xargs wc -l
text Keluaran
 2 catatan-1.txt
 1 catatan-2.txt
 3 catatan-3.txt
 6 total

Perhatikan baris total pada keluaran di atas. Karena ketiga nama berkas menjadi argumen dalam satu pemanggilan perintah, wc -l dijalankan sekali untuk semuanya dan mencetak pula jumlah gabungan ketiganya. Perilaku ini berbeda dengan penjumlahan langsung di akhir pipeline, yang hanya menerima aliran teks dan tidak pernah mengetahui nama berkasnya.

Opsi -n dan -I#

Dua opsi mengatur cara xargs membentuk argumennya. Opsi -n N membatasi jumlah argumen per proses, sehingga perintah dijalankan beberapa kali dengan kelompok kecil butir masukan. Opsi -I{} melakukan penggantian teks, yaitu setiap kemunculan {} pada argumen awal diganti dengan satu butir masukan, dan setiap butir memicu satu proses baru. Opsi -I berguna apabila argumen harus diletakkan di tengah perintah, bukan di bagian akhir.

bash
# membagi masukan dua argumen per proses dengan -n
printf 'satu\ndua\ntiga\nempat\n' | xargs -n 2 echo proses:
text Keluaran
proses: satu dua
proses: tiga empat

Keluaran di atas memperlihatkan dua pemanggilan echo karena empat butir masukan dibagi menjadi kelompok berukuran dua. Contoh berikut memakai -I{} untuk menempatkan butir masukan di tengah perintah cp. Setiap baris masukan menghasilkan satu proses cp tersendiri, dengan berkas salinan yang diberi awalan salinan-.

bash
# menyalin setiap berkas dengan pola pengganti -I{}
ls catatan-*.txt | xargs -I{} cp {} salinan-{}
ls -1 salinan-*.txt
text Keluaran
salinan-catatan-1.txt
salinan-catatan-2.txt
salinan-catatan-3.txt

Nama berkas dengan spasi dan opsi -0#

Pemecahan bawaan xargs menganggap setiap spasi sebagai pemisah argumen. Nama berkas yang mengandung spasi karenanya dipecah menjadi beberapa argumen, dan perintah yang dijalankan menunjuk ke berkas yang tidak ada. Jebakan ini mudah muncul pada berkas unggahan dan hasil ekspor, yang sering memakai spasi dalam namanya.

Solusinya adalah pasangan find -print0 dengan xargs -0. Opsi -print0 pada find mengakhiri setiap nama dengan karakter NUL, sedangkan opsi -0 pada xargs membaca masukan berdasarkan pemisah NUL itu. Karena nama berkas tidak boleh mengandung karakter NUL, pasangan ini aman untuk nama berkas apa pun. Contoh berikut memproses berkas yang namanya mengandung spasi tanpa kesalahan.

bash
# membuat berkas dengan nama mengandung spasi
printf 'isi berkas\n' > 'berkas dengan spasi.txt'
# menemukan berkas dengan -print0 lalu memprosesnya dengan aman
find . -maxdepth 1 -name 'berkas dengan spasi.txt' -print0 | xargs -0 wc -l
text Keluaran
1 ./berkas dengan spasi.txt

Paralelisme dengan opsi -P#

Secara bawaan xargs menjalankan perintahnya satu per satu. Opsi -P N mengizinkan hingga N proses berjalan bersamaan, sehingga pekerjaan yang tidak saling bergantung selesai lebih cepat pada mesin dengan banyak inti prosesor. Pekerjaan yang cocok untuk pola ini antara lain kompresi banyak berkas, konversi gambar, dan pelaksanaan pengujian per berkas. Jumlah proses yang masuk akal biasanya sekitar jumlah inti prosesor, misalnya dengan -P "$(nproc)" agar skrip menyesuaikan diri dengan mesinnya.

Satu akibat penting perlu diantisipasi, yaitu urutan keluaran antar proses tidak dijamin. Proses yang selesai lebih dulu mencetak keluarannya lebih dulu, sehingga baris keluaran dapat tertukar antar tugas. Apabila urutan penting, kumpulkan keluaran lalu urutkan dengan sort, seperti pada contoh berikut.

bash
# menjalankan empat tugas dengan dua proses paralel, lalu mengurutkan keluarannya
seq 1 4 | xargs -P 2 -I{} sh -c 'echo "tugas {} selesai"' | sort
text Keluaran
tugas 1 selesai
tugas 2 selesai
tugas 3 selesai
tugas 4 selesai

GNU parallel#

GNU parallel adalah alat dengan prinsip kerja yang sama, ditambah fitur yang lebih kaya. Fitur menonjolnya antara lain opsi –keep-order yang menjamin urutan keluaran tanpa sort, pengelompokan keluaran per job agar baris antar proses tidak bercampur, penanganan kutip yang lebih aman, serta baris progres untuk pekerjaan panjang. Perintah parallel tidak terpasang di sandbox, sehingga contoh berikut hanya ditampilkan dan tidak dijalankan.

bash
# tidak-dijalankan
# GNU parallel tidak terpasang di sandbox, contoh hanya untuk ilustrasi
seq 1 4 | parallel -j 2 echo "tugas {} selesai"

Baris {} pada GNU parallel bekerja seperti pada xargs -I{}, sedangkan flag -j 2 setara dengan -P 2. Bagi skrip yang sudah stabil menggunakan xargs -0 dan -I{}, pemindahan ke GNU parallel umumnya hanya mengganti nama perintahnya. Karena paketnya terpisah dari coreutils, GNU parallel perlu dipasang dahulu pada server dan image container yang minim paketnya.

Kesalahan umum#

Kesalahan berikut paling sering muncul saat xargs dipraktikkan, beserta perbaikannya.

  1. Masukan kosong tetap memicu eksekusi. GNU xargs menjalankan perintahnya satu kali bahkan tanpa masukan, sehingga pipeline find ... | xargs rm dapat berjalan tanpa argumen dan hanya menampilkan pesan penggunaan rm. Sertakan opsi -r (no-run-if-empty) agar perintah dilewati ketika masukannya kosong.
  2. Nama berkas berisi spasi diproses tanpa -0. Nama berkas dipecah menjadi beberapa argumen dan perintahnya menunjuk ke berkas yang tidak ada. Gunakan pasangan find -print0 dengan xargs -0 pada semua pipeline yang berbasis find.
  3. Keluaran -P dianggap berurutan. Urutan baris antar proses tidak dijamin, sehingga log gabungan tampak kacau. Urutkan keluaran dengan sort, atau beralihlah ke GNU parallel dengan –keep-order.

Ringkasan#

  • xargs mengubah masukan standar menjadi argumen baris perintah, sehingga pipeline dapat dilanjutkan ke perintah yang tidak membaca masukan standar.
  • Opsi -n membatasi argumen per proses, -I{} menempatkan butir masukan di posisi mana pun, dan -0 menangani nama berkas apa pun bersama find -print0.
  • Opsi -P N menjalankan hingga N proses secara paralel dengan urutan keluaran yang tidak dijamin.
  • GNU parallel menyediakan perilaku setara ditambah –keep-order dan pengelompokan keluaran per job.

Referensi#