Bagaimana cara menggunakan> dalam perintah xargs?

160

Saya ingin menemukan perintah bash yang akan membiarkan saya grep setiap file dalam direktori dan menulis output grep itu ke file terpisah. Dugaan saya adalah melakukan sesuatu seperti ini

ls -1 | xargs -I{} "grep ABC '{}' > '{}'.out"

tapi, sejauh yang saya tahu, xargs tidak suka tanda kutip ganda. Namun, jika saya menghapus tanda kutip ganda, maka perintah tersebut akan mengalihkan output dari seluruh perintah ke satu file yang disebut '{}'. Keluar alih-alih ke serangkaian file individual.

Adakah yang tahu cara melakukan ini menggunakan xargs? Saya hanya menggunakan skenario grep ini sebagai contoh untuk menggambarkan masalah saya dengan xargs sehingga solusi apa pun yang tidak menggunakan xargs tidak berlaku untuk saya.

Jesse Shieh
sumber

Jawaban:

201

Jangan membuat kesalahan dengan melakukan ini:

sh -c "grep ABC {} > {}.out"

Ini akan rusak dalam banyak kondisi, termasuk nama file yang funky dan tidak mungkin untuk mengutip dengan benar. Anda {}harus selalu menjadi argumen tunggal yang sepenuhnya terpisah dari perintah untuk menghindari bug injeksi kode. Yang perlu Anda lakukan, adalah ini:

xargs -I{} sh -c 'grep ABC "$1" > "$1.out"' -- {}

Berlaku untuk xargsdan juga find.

Omong-omong, jangan pernah menggunakan xargs tanpa -0opsi (kecuali untuk penggunaan interaktif satu kali yang sangat jarang dan terkontrol di mana Anda tidak khawatir akan menghancurkan data Anda).

Juga jangan parsing ls. Pernah. Gunakan globbing atau findsebagai gantinya:http://mywiki.wooledge.org/ParsingLs

Gunakan finduntuk semua yang membutuhkan rekursi dan putaran sederhana dengan bola untuk yang lainnya:

find /foo -exec sh -c 'grep "$1" > "$1.out"' -- {} \;

atau non-rekursif:

for file in *; do grep "$file" > "$file.out"; done

Perhatikan penggunaan tanda kutip yang tepat.

lununath
sumber
Terpilih tetapi ragu regd. tidak menggunakan xargstanpa -0: ini hanya berlaku ketika Anda mem find- pipe output dengan xargs, kan? ketika saya melakukan xargs -a <input_file>bagaimana saya akan menggunakan ini? Sebagian besar perintah menyukai grepkeluaran dengan \ndan bukan \0.Satu-satunya cara saya dapat memikirkan untuk mengatasi ini adalah dengan menggunakan trlagi untuk memperbaikinya mungkin. Tetapi mengapa penting untuk menggunakannya hanya dengan -0?
legends2k
3
@ legends2k karena ketika Anda tidak menggunakan -0, xargsakan mengambil nama file Anda dan memecah semua spasi, tanda kutip dan garis miring terbalik di dalamnya. Anda harus melupakan xargssebagai alat. Jika Anda memiliki garis, gunakan bash loop untuk mengulangi garis while read line; do <command> "$REPLY"; done < file-with-linescommand | while ...
:,
1
Wow, tidak tahu tentang itu, terima kasih untuk detailnya! Jadi untuk portabilitas (karena tidak semua xargsGNU), xargsperlu dihindari kecuali seseorang dapat menggunakannya -0. Terima kasih.
legends2k
1
Meskipun saya menghargai penjelasan terinci untuk kasus penggunaan khusus ini, pertanyaannya adalah tentang mengarahkan ulang output xargs, yang tidak selalu melibatkan penguraian lsatau penggunaan sh -c. Ini tidak menjawab pertanyaan sedikit pun, tetapi merupakan hasil google pertama untuk pertanyaan itu, hanya menambah kebingungan.
pandasauce
1
@Ihunath, Hai, jawaban Anda bekerja baik untuk saya. Tetapi bisakah Anda memberikan penjelasan atau tautan terperinci xargs -I{} sh -c 'grep ABC "$1" > "$1.out"' -- {}? Terutama, aturan tanda kutip (dobel) dan simbol "-" di akhir. Terima kasih
Scott Yang
40

Solusi tanpa xargsadalah sebagai berikut:

find . -mindepth 1 -maxdepth 1 -type f -exec sh -c "grep ABC '{}' > '{}.out'" \;

... dan hal yang sama dapat dilakukan dengan xargs , ternyata:

ls -1 | xargs -I {} sh -c "grep ABC '{}' > '{}.out'"

Sunting : kutipan tunggal ditambahkan setelah komentar oleh lhunath .

Stephan202
sumber
Dia bilang dia ingin menggunakan xargs. Saya memposting solusi tanpa itu juga, tetapi dihapus begitu saya melihat bahwa dia membutuhkan xargs.
Zifre
Kamu benar. Alasan saya memposting jawaban saya adalah bahwa lebih baik memiliki solusi alternatif untuk menyelesaikan pekerjaan daripada tidak sama sekali. Ternyata itu menempatkan saya di jalur yang benar untuk menemukan jawaban yang diinginkan (yaitu, trik sh -c).
Stephan202
14

Saya menganggap contoh Anda hanyalah contoh dan bahwa Anda mungkin perlu> untuk hal-hal lain. GNU Parallel http://www.gnu.org/software/parallel/ dapat menjadi bantuan Anda. Tidak perlu mengutip tambahan selama nama file Anda tidak mengandung \ n:

ls | parallel "grep ABC {} > {}.out"

Jika Anda memiliki nama file dengan \ n di dalamnya:

find . -print0 | parallel -0 "grep ABC {} > {}.out"

Sebagai bonus tambahan Anda menjalankan pekerjaan secara paralel.

Tonton video intro untuk mempelajari lebih lanjut: http://pi.dk/1

Instalasi 10 detik akan mencoba melakukan instalasi penuh; jika itu gagal, instalasi pribadi; jika gagal, instalasi minimal:

$ (wget -O - pi.dk/3 || lynx -source pi.dk/3 || curl pi.dk/3/ || \
   fetch -o - http://pi.dk/3 ) > install.sh
$ sha1sum install.sh | grep 3374ec53bacb199b245af2dda86df6c9
12345678 3374ec53 bacb199b 245af2dd a86df6c9
$ md5sum install.sh | grep 029a9ac06e8b5bc6052eac57b2c3c9ca
029a9ac0 6e8b5bc6 052eac57 b2c3c9ca
$ sha512sum install.sh | grep f517006d9897747bed8a4694b1acba1b
40f53af6 9e20dae5 713ba06c f517006d 9897747b ed8a4694 b1acba1b 1464beb4
60055629 3f2356f3 3e9c4e3c 76e3f3af a9db4b32 bd33322b 975696fc e6b23cfb
$ bash install.sh

Jika Anda perlu memindahkannya ke server, yang tidak menginstal GNU Parallel, coba parallel --embed.

Ole Tange
sumber