Kapan xargs dibutuhkan?

134

The xargsperintah selalu membingungkan saya. Apakah ada aturan umum untuk itu?

Perhatikan dua contoh di bawah ini:

$ \ls | grep Cases | less

mencetak file yang cocok dengan 'Kasus', tetapi mengubah perintah untuk touchakan membutuhkan xargs:

$ \ls | grep Cases | touch
touch: missing file operand
Try `touch --help' for more information.

$ \ls | grep Cases | xargs touch
Zaid
sumber

Jawaban:

143

Perbedaannya terletak pada data apa yang diterima oleh program target.

Jika Anda hanya menggunakan pipa, ia menerima data pada STDIN (aliran input standar) sebagai tumpukan data mentah yang dapat disortir melalui satu baris pada satu waktu. Namun beberapa program tidak menerima perintah mereka pada standar di, mereka berharap itu akan dijabarkan dalam argumen ke perintah. Misalnya touchmengambil nama file sebagai parameter pada baris perintah seperti: touch file1.txt.

Jika Anda memiliki program yang output nama-nama file pada standar keluar dan ingin menggunakannya sebagai argumen untuk touch, Anda harus menggunakan xargsyang membaca data aliran STDIN dan mengkonversi setiap baris ke ruang angkasa dipisahkan argumen untuk perintah.

Dua hal ini setara:

# touch file1.txt
# echo file1.txt | xargs touch

Jangan gunakan xargskecuali Anda tahu persis apa yang dilakukannya dan mengapa itu diperlukan. Sering terjadi bahwa ada cara yang lebih baik untuk melakukan pekerjaan daripada menggunakan xargsuntuk memaksa konversi. Proses konversi juga penuh dengan jebakan potensial seperti melarikan diri dan ekspansi kata dll.

Caleb
sumber
2
Peringatan itu terasa sedikit mengganggu bagi saya. Dari dua opsi umum untuk mendapatkan aliran ke baris perintah ( xargsdan $(...)), xargs jauh lebih aman daripada penggantian perintah. Dan saya tidak ingat pernah menemukan nama file yang sah dengan baris baru di dalamnya. Bukankah masalah pelarian dan perluasan kata dengan substitusi perintah, bukan xargs?
camh
6
@camh: Mereka jebakan potensial dengan keduanya. Dalam shell, Anda harus khawatir tentang nama file yang terpecah pada spasi, tab, dan baris baru. Dalam xargs, Anda hanya perlu khawatir tentang baris baru. Dalam xargs, jika output Anda diformat dengan benar, Anda dapat membagi kata / nama file pada karakter NUL ( xargs -0), yang berguna bersamaan find -print0.
Ken Bloom
Apakah xargsmemanggil program melalui shell dengan spasi yang terpisah, atau apakah itu benar-benar membangun daftar argumen secara internal (mis. Untuk digunakan dengan execv/ execp)?
detly
1
Itu membangunnya secara internal dan menggunakan execvp, jadi aman. Juga, GNU xargs (seperti yang digunakan di Linux dan beberapa lainnya) memungkinkan Anda menentukan baris baru dengan pembatas Anda -d \n, meskipun BSD xargs (OSX et al) tampaknya tidak mendukung opsi ini.
lembut
72

Untuk memperluas jawaban yang sudah disediakan, xargsdapat melakukan satu hal keren yang menjadi semakin penting dalam lanskap komputasi multicore dan terdistribusi saat ini: dapat melakukan pekerjaan paralel.

Sebagai contoh:

$ find . -type f -name '*.wav' -print0 |xargs -0 -P 3 -n 1 flac -V8

akan menyandikan * .wav => * .flac, menggunakan tiga proses sekaligus ( -P 3).

amphetamachine
sumber
Wow. Saya seharusnya tahu ini seminggu yang lalu ketika saya melakukan hal yang persis sama (kecuali menggunakan OGG) dengan 50GiB WAV. :)
Alois Mahdal
mengapa tidak menggunakan parameter -exec yang ditemukan memiliki?
Evgeny
3
@ Evgeny -execParameter tidak akan memproses pekerjaan paralel.
amphetamachine
Bagus untuk dicatat bahwa -0argumen untukxargs membuatnya mempertimbangkan NULLkarakter sebagai pembatas item input. find -print0menampilkan item-item yang dibatasi-NULL. Ini adalah praktik yang bagus untuk nama file yang mungkin mengandung spasi, tanda kutip, atau karakter khusus lainnya.
Dan Dascalescu
24

xargs sangat berguna ketika Anda memiliki daftar filepath di stdin dan ingin melakukan sesuatu dengannya. Sebagai contoh:

$ git ls-files "*.tex" | xargs -n 1 sed -i "s/color/colour/g"

Mari kita periksa langkah demi langkah:

$ git ls-files "*.tex"
tex/ch1/intro.tex
tex/ch1/motivation.tex
....

Dengan kata lain, input kami adalah daftar jalur yang ingin kami lakukan sesuatu.

Untuk mengetahui apa yang dilakukan xargs dengan path ini, trik yang bagus adalah menambahkan echosebelum perintah Anda, seperti:

$ git ls-files "*.tex" | xargs -n 1 echo sed -i "s/color/colour/g"
sed -i "s/color/colour/g" tex/ch1/intro.tex
sed -i "s/color/colour/g" tex/ch1/motivation.tex
....

The -n 1Argumen akan membuat xargs berubah setiap baris menjadi perintah sendiri. The sed -i "s/color/colour/g"perintah akan mengganti semua kejadian dari colordengan colouruntuk file yang ditentukan.

Perhatikan bahwa ini hanya berfungsi jika Anda tidak memiliki spasi di jalur Anda. Jika Anda melakukannya, Anda harus menggunakan jalur terminasi nol sebagai input ke xargs dengan melewati -0flag. Contoh penggunaannya adalah:

$ git ls-files -z "*.tex" | xargs -0 -n 1 sed -i "s/color/colour/g"

Yang melakukan hal yang sama seperti apa yang kami jelaskan di atas, tetapi juga berfungsi jika salah satu jalur memiliki ruang di dalamnya.

Ini berfungsi dengan perintah apa pun yang menghasilkan nama file sebagai keluaran seperti findatau locate. Jika Anda menggunakannya di repositori git dengan banyak file, mungkin lebih efisien untuk menggunakannya dengan git grep -ldaripada git ls-files, seperti:

$ git grep -l "color" "*.tex" | xargs -n 1 sed -i "s/color/colour/g"

The git grep -l "color" "*.tex"perintah akan memberikan daftar "* tex" file yang berisi frasa "warna".

Sverre Rabbelier
sumber
1
Benar, tetapi jika Anda telah mempelajari ini, Anda juga harus belajar. Mengapa mengulangi hasil praktik buruk?
Wildcard
6

Argumen pertama Anda menggambarkan perbedaan dengan cukup baik.

\ls | grep Cases | lessmemungkinkan Anda menelusuri daftar nama file yang dihasilkan oleh lsdan grep. Tidak masalah mereka kebetulan nama file, mereka hanya beberapa teks.

\ls | grep Cases | xargs lessmemungkinkan Anda menelusuri file yang namanya dihasilkan oleh bagian pertama dari perintah. xargsmengambil daftar nama file sebagai input dan perintah dalam baris perintah, dan menjalankan perintah dengan nama file pada nya baris perintah.

Ketika mempertimbangkan untuk menggunakan xargs, ingatlah bahwa ia mengharapkan input diformat dengan cara yang aneh: whitespace-delimited, with \, 'dan "digunakan untuk mengutip (dengan cara yang tidak biasa, karena \tidak ada kutipan dalam khusus). Hanya gunakan xargsjika Anda nama file Anda tidak mengandung spasi atau \'".

Gilles
sumber
@Gilles: xargs memiliki -0, --nullopsi untuk mengatasi masalah spasi (kemungkinan besar saya mengetahui hal itu dari Anda :), jadi saya berasumsi bahwa Anda merujuk pada panggilan tidak-pilihan xarg, tapi saya bingung dengan referensi Anda pada tanda kutip. Apakah Anda memiliki tautan atau contoh tentang itu? .. (ps. | xargs lessadalah "tipuan" berguna +1 .. terima kasih ..
Peter.O
4

Dalam contoh Anda, Anda tidak perlu menggunakan xargssama sekali karena findakan melakukan dengan tepat dan aman apa yang ingin Anda lakukan.

Apa yang ingin Anda gunakan findadalah:

find -maxdepth 1 -name '*Cases*' -exec touch {} +

Dalam contoh ini -maxdepth 1berarti hanya mencari di direktori saat ini, jangan turun ke subdirektori mana pun; secara default find akan terlihat di semua subdirektori (yang sering kali Anda inginkan) kecuali Anda membatasi dengan maxdepth. The {}adalah nama file yang akan mendapatkan diganti di tempatnya dan +merupakan salah satu dari dua end-of-perintah penanda, makhluk lainnya ;. Perbedaan di antara mereka adalah itu ;berarti exec perintah pada setiap file satu per satu, sedangkan +berarti exec perintah pada semua file sekaligus. Perhatikan, bagaimanapun, bahwa shell Anda mungkin akan mencoba menafsirkannya ;sendiri, jadi Anda harus menghindarinya dengan salah satu \;atau ';'. Ya, findmemiliki sejumlah gangguan kecil seperti ini, tetapi kekuatannya lebih dari menebusnya.

Kedua finddan xargsrumit untuk belajar pada awalnya. Untuk membantu Anda belajar, xargscoba gunakan opsi -patau --interactiveyang akan menunjukkan kepada Anda perintah yang akan dijalankan dan menanyakan apakah Anda ingin menjalankannya atau tidak.

Demikian pula dengan findAnda dapat menggunakan -okdi tempat -execuntuk meminta Anda apakah Anda ingin menjalankan perintah atau tidak.

Ada saat-saat ketika findtidak akan dapat melakukan semua yang Anda inginkan dan di situlah xargsmasuk. -execPerintah hanya akan menerima satu contoh {}muncul, jadi jika Anda akan mendapatkan kesalahan dengan find -type f -exec cp {} {}.bak \;begitu Anda bisa melakukannya seperti :find -type f -print0 | xargs -0 -l1 -IX cp X X.bak

Anda dapat mempelajari lebih lanjut tentang Menjalankan Perintah di manual GNU Findutils .

Juga, saya menyebutkan bahwa findmelakukan apa yang Anda inginkan dengan aman karena ketika Anda berurusan dengan file, Anda akan menemukan spasi dan karakter lain yang akan menyebabkan masalah xargskecuali Anda menggunakan opsi -0atau --nullbersama dengan sesuatu yang menghasilkan item input yang diakhiri oleh karakter nol sebagai gantinya ruang putih.

aculich
sumber
@Wildcard nama file dengan spasi atau karakter seperti 'atau "bisa bermasalah, sedangkan findakan menangani kasus-kasus itu tanpa masalah.
aculich
Ya saya tahu. Lihat jawaban saya untuk pertanyaan terkait . Saya mungkin harus mengulangi pertanyaan itu menjadi pernyataan di komentar di atas, atau menambahkan frasa "Lihat pertanyaan ..." di depannya. : D
Wildcard
1

xargs(bersama dengan find, sort, du, uniq, perldan beberapa orang lainnya) menerima saklar baris perintah untuk mengatakan "STDIN memiliki daftar file, dipisahkan oleh NUL (0x00) byte". Ini membuatnya mudah untuk menangani nama file dengan spasi dan karakter lucu lainnya di dalamnya. Nama file tidak mengandung NUL.

waltinator
sumber
2
Saya pikir maksud Anda "nama file tidak boleh mengandung nulls."
amphetamachine