Jalankan perintah sekali per baris input pipa?


162

Saya ingin menjalankan perintah java sekali untuk setiap pertandingan ls | grep pattern -. Dalam hal ini, saya pikir saya bisa melakukannya find pattern -exec java MyProg '{}' \;tetapi saya ingin tahu tentang kasus umum - apakah ada cara mudah untuk mengatakan "jalankan perintah sekali untuk setiap baris input standar"? (Dalam ikan atau bash.)

Jawaban:


92

Itu yang xargsdilakukannya.

... | xargs command

25
Tidak terlalu. printf "foo bar\nbaz bat" | xargs echo wheeakan menghasilkan whee foo bar baz bat. Mungkin menambahkan opsi -Latau -n?
Jander

3
@ Jander Pertanyaannya agak umum, jadi saya memberikan alat umum. Benar, Anda harus menyesuaikan perilakunya dengan opsi tergantung pada keadaan tertentu.
Keith

4
... | tr '\ n' '\ 0' | xargs -0
vrdhn

7
seperti, "keadaan khusus yang memberikan jawaban yang tepat untuk pertanyaan". :)
mattdm

7
Jika Anda ingin melihat cara yang tepat untuk melakukan ini dengan xargs, lihat jawaban saya di bawah ini.
Michael Goldshteyn

167

Jawaban yang diterima memiliki ide yang tepat, tetapi kuncinya adalah untuk lulus xargsdengan -n1switch, yang berarti "Jalankan perintah sekali per baris output:"

cat file... | xargs -n1 command

Atau, untuk file input tunggal Anda dapat menghindari pipa dari catseluruhnya dan hanya pergi dengan:

<file xargs -n1 command

1
Yang juga menarik adalah kemampuan xargsuntuk tidak berjalan jika stdinkosong --no-run-if-empty -r:: Jika input standar tidak mengandung nonblank, jangan jalankan perintah. Biasanya, perintah dijalankan sekali walaupun tidak ada input. Opsi ini adalah ekstensi GNU.
Ronan Jouchet

4
Bagaimana Anda mengakses jalur di dalam command?
BT

Ini adalah penggunaan xargs yang benar. Tanpa -n1, ini hanya bekerja pada perintah yang memperlakukan daftar parameter sebagai banyak pemanggilan yang tidak semuanya dilakukan.
masterxilo

3
printf "foo bar \ nbaz bat" | xargs -n1 gema whee dibagi dengan kata-kata dan bukan dengan garis
Gismo Ranas

112

Di Bash atau shell Bourne-style lainnya (abu, ksh, zsh, ...):

while read -r line; do command "$line"; done

read -rmembaca satu baris dari input standar ( readtanpa -rmenginterpretasikan garis miring terbalik, Anda tidak menginginkannya). Dengan demikian Anda dapat melakukan salah satu dari yang berikut:

$ command | while read -r line; do command "$line"; done  

$ while read -r line; do command "$line"; done <file

6
Ketika saya mencobanya tail -f syslog | grep -e something -e somethingelse| while read line; do echo $line; donetidak berhasil. Ini bekerja dengan file yang disalurkan ke whileloop, bekerja dengan hanya tail -f, bekerja dengan adil grep, tetapi tidak dengan kedua pipa. Memberikan grepyang --line-bufferedpilihan membuatnya bekerja

Ini berfungsi juga ketika setiap baris perlu dikirim ke stdin:command | while read -r line; do echo "$line" | command ; done
Den

21

Saya setuju dengan Keith, xargs adalah alat paling umum untuk pekerjaan itu.

Saya biasanya menggunakan pendekatan 3 langkah.

  • lakukan hal-hal dasar sampai Anda memiliki sesuatu yang ingin Anda kerjakan
  • siapkan baris dengan awk sehingga mendapat sintaks yang benar
  • lalu biarkan xargs menjalankannya, mungkin dengan bantuan bash.

Ada cara yang lebih kecil dan lebih cepat, tetapi cara ini hampir selalu berhasil.

Contoh sederhana:

ls | 
grep xls | 
awk '{print "MyJavaProg --arg1 42 --arg2 "$1"\0"}' | 
xargs -0 bash -c

2 baris pertama memilih beberapa file untuk dikerjakan, kemudian awk menyiapkan string yang bagus dengan perintah untuk dieksekusi dan beberapa argumen dan $ 1 adalah input kolom pertama dari pipa. Dan akhirnya saya memastikan bahwa xargs mengirimkan string ini ke bash yang baru saja menjalankannya.

Ini sedikit berlebihan, tetapi resep ini telah membantu saya di banyak tempat karena sangat fleksibel.


6
Catatan, xargs -0gunakan byte nol sebagai pemisah rekaman, jadi pernyataan cetak awk Anda seharusnyaprintf("MyJavaProg --args \"%s\"\0",$1)
glenn jackman

@glenn: Merindukan null char, akan memperbarui jawabannya
Johan

@ Johan bukan masalah besar, tetapi jika Anda menggunakan awkAnda dapat memilikinya melakukan pertandingan pola dan melewatkan grep eg,ls | awk '/xls/ {print...
Eric Renouf

15

GNU Parallel dibuat untuk tugas semacam itu. Penggunaan paling sederhana adalah:

cat stuff | grep pattern | parallel java MyProg

Tonton video intro untuk mempelajari lebih lanjut: http://www.youtube.com/watch?v=OpaiGYxkSuQ


1
Tidak ada kebutuhan nyata untuk di catsini karena grepdapat langsung membaca file
Eric Renouf


1
Terima kasih atas tautannya, saya tidak selalu setuju bahwa itu lebih mudah dibaca, tetapi senang mengetahui itu dianggap terlepas. Saya hanya akan sedikit berdalih bahwa tautan tersebut tidak benar-benar berlaku di sini karena alternatifnya tidak benar-benar < stuff grep patterntetapi grep pattern stufftanpa redirection atau cat sama sekali. Namun, itu tidak secara material mengubah argumen Anda dan jika Anda pikir lebih jelas untuk selalu menggunakan hal-hal dalam pipa yang dimulai dengan cat, maka berkuasa untuk Anda
Eric Renouf

8

Juga, while readloop dalam cangkang ikan (saya berasumsi Anda ingin cangkang ikan, mengingat Anda menggunakan tag ).

command | while read line
    command $line
end

Beberapa poin yang perlu diperhatikan.

  • readtidak mengambil -rargumen, dan itu tidak menafsirkan backslash Anda, untuk membuat use case paling umum mudah.
  • Anda tidak perlu mengutip $line, karena tidak seperti bash, ikan tidak memisahkan variabel dengan spasi.
  • commanddengan sendirinya adalah kesalahan sintaks (untuk menangkap penggunaan argumen placeholder seperti itu). Ganti dengan perintah asli.

Tidak whileperlu dipasangkan dengan do& donebukan end?
aff

@aff Ini khusus tentang shell ikan, yang memiliki sintaks yang berbeda.
Konrad Borowski

Ah, jadi itu maksud ikan itu.
aff

6

Jika Anda perlu mengontrol di mana tepatnya argumen input dimasukkan ke dalam baris perintah Anda atau jika Anda perlu mengulanginya beberapa kali maka Anda perlu menggunakannya xargs -I{}.

CONTOH 1

Buat struktur folder kosong di another_folderyang mencerminkan subfolder di direktori saat ini:

    ls -1d ./*/ | xargs -I{} mkdir another_folder/{}
CONTOH # 2

Terapkan operasi pada daftar file yang berasal dari stdin, dalam hal ini buat salinan dari setiap .htmlfile dengan menambahkan .bakekstensi:

    find . -iname "*.html" | xargs -I{} cp {} {}.bak

Dari xargshalaman manual untuk MacOS / BSD :

 -I replstr
         Execute utility for each input line, replacing one or more occurrences of
         replstr in up to replacements (or 5 if no -R flag is specified) arguments
         to utility with the entire line of input.  The resulting arguments, after
         replacement is done, will not be allowed to grow beyond 255 bytes; this is
         implemented by concatenating as much of the argument containing replstr as
         possible, to the constructed arguments to utility, up to 255 bytes.  The
         255 byte limit does not apply to arguments to utility which do not contain
         replstr, and furthermore, no replacement will be done on utility itself.
         Implies -x.

xargsHalaman manual Linux :

   -I replace-str
          Replace  occurrences of replace-str in the initial-
          arguments with names read from standard input.  Al
          so,  unquoted  blanks do not terminate input items;
          instead the separator  is  the  newline  character.
          Implies -x and -L 1.

1

Ketika berhadapan dengan input yang berpotensi tidak bersih, saya suka melihat seluruh pekerjaan 'terbilang' baris demi baris untuk inspeksi visual sebelum saya menjalankannya (terutama ketika sesuatu yang merusak seperti membersihkan kotak surat orang).

Jadi yang saya lakukan adalah membuat daftar parameter (mis. Nama pengguna), memasukkannya ke file dengan mode satu-catatan-per-baris, seperti ini:

johndoe  
jamessmith  
janebrown  

Lalu saya membuka daftar vim, dan memotong-motongnya dengan mencari dan mengganti ekspresi sampai saya mendapatkan daftar perintah lengkap yang perlu dijalankan, seperti ini:

/bin/rm -fr /home/johndoe  
/bin/rm -fr /home/jamessmith 

Dengan cara ini jika regex Anda tidak lengkap, Anda akan melihat perintah apa yang akan memiliki masalah potensial (mis. /bin/rm -fr johnnyo connor). Dengan cara ini Anda dapat membatalkan regex Anda, dan coba lagi dengan versi yang lebih andal. Nama mangling terkenal karena hal ini, karena sulit untuk mengurus semua kasus tepi seperti Van Gogh, O'Connors, St. Clair, Smith-Wesson.

Memiliki set hlsearchberguna untuk melakukan ini vim, karena akan menyoroti semua pertandingan, sehingga Anda dapat dengan mudah melihat jika tidak cocok, atau cocok dengan cara yang tidak disengaja.

Setelah regex Anda sempurna dan menangkap semua case yang dapat Anda uji / pikirkan, maka saya biasanya mengonversinya menjadi ekspresi sed sehingga dapat sepenuhnya otomatis untuk dijalankan lagi.

Untuk kasus di mana jumlah baris input mencegah Anda melakukan inspeksi visual, saya sangat merekomendasikan untuk mengulangi perintah ke layar (atau lebih baik, log) sebelum dieksekusi, jadi jika kesalahan keluar, Anda tahu persis perintah yang menyebabkan itu gagal. Kemudian Anda dapat kembali ke regex asli dan menyesuaikannya sekali lagi.


0

Jika suatu program mengabaikan pipa tetapi menerima file sebagai argumen, maka Anda bisa mengarahkannya ke file khusus /dev/stdin.

Saya tidak terbiasa dengan java, tapi di sini adalah contoh bagaimana Anda akan melakukannya untuk bash:

$ echo $'pwd \n cd / \n pwd' |bash /dev/stdin
/home/rolf
/

$ Diperlukan untuk bash menerjemahkan \nke dalam baris baru. Saya tidak yakin mengapa.



0

Di sini, copypaste Anda dapat langsung digunakan:

cat list.txt | xargs -I{} command parameter {} parameter

Item dari daftar akan diletakkan di tempat {} berada dan seluruh perintah dan parameter akan digunakan apa adanya.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.