Buat xargs jalankan perintah sekali untuk setiap baris input


341

Bagaimana saya bisa membuat xargs mengeksekusi perintah tepat sekali untuk setiap baris input yang diberikan? Perilaku defaultnya adalah memotong-motong baris dan menjalankan perintah sekali, melewati beberapa baris ke setiap instance.

Dari http://en.wikipedia.org/wiki/Xargs :

find / path -type f -print0 | xargs -0 rm

Dalam contoh ini, temukan umpan input xargs dengan daftar panjang nama file. xargs kemudian membagi daftar ini menjadi sublists dan memanggil rm satu kali untuk setiap sublist. Ini lebih efisien daripada versi yang setara secara fungsional ini:

find / path -type f -exec rm '{}' \;

Saya tahu bahwa find memiliki flag "exec". Saya hanya mengutip contoh ilustratif dari sumber lain.


4
Dalam contoh yang Anda berikan, find /path -type f -deleteakan lebih efisien :)
tzot

cobalah untuk tidak menggunakan xargs ...
Naib

6
OP, saya tahu pertanyaan ini sudah sangat lama, tetapi masih muncul di Google dan IMHO jawaban yang diterima salah. Lihat jawaban saya yang lebih panjang di bawah.
Tobia

Silakan pertimbangkan untuk mengalihkan penerimaan Anda ke jawaban @ Tobia yang jauh lebih baik. Jawaban yang diterima tidak menangani spasi dalam nama dan tidak mengizinkan banyak argumen pada perintah xargs yang merupakan salah satu fitur utama xargs.
Gray

Jawaban:


392

Berikut ini hanya akan berfungsi jika Anda tidak memiliki spasi di input Anda:

xargs -L 1
xargs --max-lines=1 # synonym for the -L option

dari halaman manual:

-L max-lines
          Use at most max-lines nonblank input lines per command line.
          Trailing blanks cause an input line to be logically continued  on
          the next input line.  Implies -x.

13
Bagi saya itu bisa xargs -n 1seperti yang Anda berikan menunjukkan "daftar argumen terlalu panjang".
Wernight

19
Jika MAX-LINESdihilangkan, defaultnya adalah 1, xargs -lsudah cukup. Lihat info xargs.
Thor

3
@Wernight: "-n1" tidak memberikan 1 doa per baris input. mungkin jalur input Anda terlalu panjang. Demo: echo "foo bar" | xargs -n1 echo. karenanya jika Anda menyambungkan hal-hal seperti 'ls', itu tidak akan menangani ruang dengan baik.
gatoatigrado

8
Ini salah. -L 1tidak menjawab pertanyaan asli, dan -n 1hanya melakukannya dalam salah satu interpretasi yang mungkin. Lihat jawaban panjang saya di bawah ini.
Tobia

2
@ Tokia: Ini menjawab pertanyaan awal, yang cukup spesifik tentang jalur input. Itulah tepatnya -L 1. Bagi saya, OP tampaknya berusaha menghindari perilaku chunking default, dan karena ini diterima, saya menganggap saya benar. Jawaban Anda membahas kasus penggunaan yang sedikit berbeda di mana Anda menginginkan perilaku chunking juga.
Draemon

207

Menurut saya semua jawaban yang ada pada halaman ini salah, termasuk yang ditandai sebagai benar. Itu bermula dari fakta bahwa pertanyaan itu kata-kata yang ambigu.

Ringkasan:   Jika Anda ingin menjalankan perintah "tepat sekali untuk setiap baris input yang diberikan," melewati seluruh baris (tanpa baris baru) ke perintah sebagai argumen tunggal, maka ini adalah cara terbaik yang kompatibel dengan UNIX untuk melakukannya:

... | tr '\n' '\0' | xargs -0 -n1 ...

GNU xargsmungkin atau mungkin tidak memiliki ekstensi berguna yang memungkinkan Anda untuk menghapus tr, tetapi mereka tidak tersedia pada OS X dan sistem UNIX lainnya.

Sekarang untuk penjelasan panjangnya ...


Ada dua masalah yang harus diperhatikan saat menggunakan xargs:

  1. bagaimana cara membagi input menjadi "argumen"; dan
  2. berapa banyak argumen untuk lulus perintah anak pada suatu waktu.

Untuk menguji perilaku xargs, kita memerlukan utilitas yang menunjukkan berapa kali dieksekusi dan dengan berapa banyak argumen. Saya tidak tahu apakah ada utilitas standar untuk melakukan itu, tetapi kita dapat mengkodekannya dengan mudah di bash:

#!/bin/bash
echo -n "-> "; for a in "$@"; do echo -n "\"$a\" "; done; echo

Dengan asumsi Anda menyimpannya showdi direktori Anda saat ini dan membuatnya dapat dieksekusi, berikut ini cara kerjanya:

$ ./show one two 'three and four'
-> "one" "two" "three and four" 

Sekarang, jika pertanyaan aslinya benar-benar tentang poin 2. di atas (seperti yang saya pikirkan, setelah membacanya beberapa kali) dan harus dibaca seperti ini (perubahan dicetak tebal):

Bagaimana saya bisa membuat xargs mengeksekusi perintah tepat sekali untuk setiap argumen input yang diberikan? Perilaku default-nya adalah memotong input menjadi argumen dan menjalankan perintah sesedikit mungkin , meneruskan beberapa argumen ke setiap instance.

maka jawabannya adalah -n 1.

Mari kita bandingkan perilaku default xargs, yang membagi input sekitar whitespace dan memanggil perintah sesering mungkin:

$ echo one two 'three and four' | xargs ./show 
-> "one" "two" "three" "and" "four" 

dan perilakunya dengan -n 1:

$ echo one two 'three and four' | xargs -n 1 ./show 
-> "one" 
-> "two" 
-> "three" 
-> "and" 
-> "four" 

Jika, di sisi lain, pertanyaan awal adalah tentang poin 1. input terpecah dan harus dibaca seperti ini (banyak orang yang datang ke sini tampaknya berpikir itulah masalahnya, atau membingungkan kedua masalah):

Bagaimana saya bisa membuat xargs menjalankan perintah dengan tepat satu argumen untuk setiap baris input yang diberikan? Perilaku standarnya adalah memotong garis di sekitar spasi putih .

maka jawabannya lebih halus.

Orang akan berpikir itu -L 1bisa membantu, tetapi ternyata itu tidak mengubah parsing argumen. Itu hanya mengeksekusi perintah sekali untuk setiap baris input, dengan argumen sebanyak yang ada di baris input:

$ echo $'one\ntwo\nthree and four' | xargs -L 1 ./show 
-> "one" 
-> "two" 
-> "three" "and" "four" 

Tidak hanya itu, tetapi jika garis berakhir dengan spasi, itu ditambahkan ke yang berikutnya:

$ echo $'one \ntwo\nthree and four' | xargs -L 1 ./show 
-> "one" "two" 
-> "three" "and" "four" 

Jelas, -Lini bukan tentang mengubah cara xargs membagi input menjadi argumen.

Satu-satunya argumen yang melakukannya secara lintas platform (tidak termasuk ekstensi GNU) adalah -0, yang membagi input di sekitar byte NUL.

Lalu, itu hanya masalah menerjemahkan baris baru ke NUL dengan bantuan tr:

$ echo $'one \ntwo\nthree and four' | tr '\n' '\0' | xargs -0 ./show 
-> "one " "two" "three and four" 

Sekarang argumen parsing terlihat baik-baik saja, termasuk spasi spasi tambahan.

Akhirnya, jika Anda menggabungkan teknik ini dengan -n 1, Anda mendapatkan tepat satu eksekusi perintah per baris input, input apa pun yang Anda miliki, yang mungkin merupakan cara lain untuk melihat pertanyaan asli (mungkin yang paling intuitif, diberi judul):

$ echo $'one \ntwo\nthree and four' | tr '\n' '\0' | xargs -0 -n1 ./show 
-> "one " 
-> "two" 
-> "three and four" 

Sepertinya ini adalah jawaban yang lebih baik. Namun, saya masih tidak begitu mengerti apa perbedaan antara -L dan -n ... dapatkah Anda menjelaskan lebih banyak?
olala

5
@olala -Lmengeksekusi perintah satu kali per baris input (tetapi spasi di akhir baris menggabungkannya ke baris berikutnya, dan baris tersebut masih dibagi menjadi argumen sesuai dengan spasi putih); saat -nmenjalankan perintah sekali per argumen input. Jika Anda menghitung jumlah ->dalam contoh output, itu adalah berapa kali script ./showdieksekusi.
Tobia

saya melihat! tidak menyadari ada ruang di ujung garis yang menghubungkannya ke baris berikutnya. Terima kasih!
olala

4
GNU xargsmungkin atau mungkin tidak memiliki ekstensi yang berguna yang memungkinkan Anda untuk menghapusnya.tr Ini memiliki ekstensi yang sangat berguna; from xargs --help- -d, --delimiter = CHARACTER item dalam aliran input dipisahkan oleh CHARACTER, bukan oleh spasi; menonaktifkan pemrosesan quote dan backslash dan pemrosesan EOF yang logis
Piotr Dobrogost

Jawaban ini sepertinya membingungkan -L. -Ltidak mengatakan berapa kali untuk mengeksekusi skrip per baris, ia mengatakan berapa banyak baris input data untuk dikonsumsi sekaligus.
Moberg

22

Jika Anda ingin menjalankan perintah untuk setiap baris (yaitu hasil) yang berasal find, lalu untuk apa Anda membutuhkannya xargs?

Mencoba:

find path -type f -exec your-command {} \;

di mana literal {}diganti dengan nama file dan literal \;diperlukan untuk findmengetahui bahwa perintah kustom berakhir di sana.

EDIT:

(setelah pengeditan pertanyaan Anda mengklarifikasi yang Anda ketahui -exec)

Dari man xargs:

-L max-lines
Gunakan paling banyak baris max-line input nonblank per baris perintah. Trailing blanks menyebabkan jalur input diteruskan secara logis pada jalur input berikutnya. Tersirat -x.

Perhatikan bahwa nama file yang diakhiri dengan kosong akan menyebabkan masalah jika Anda menggunakan xargs:

$ mkdir /tmp/bax; cd /tmp/bax
$ touch a\  b c\  c
$ find . -type f -print | xargs -L1 wc -l
0 ./c
0 ./c
0 total
0 ./b
wc: ./a: No such file or directory

Jadi, jika Anda tidak peduli dengan -execopsi tersebut, Anda sebaiknya menggunakan -print0dan -0:

$ find . -type f -print0 | xargs -0L1 wc -l
0 ./c
0 ./c
0 ./b
0 ./a

17

Bagaimana saya bisa membuat xargs mengeksekusi perintah tepat sekali untuk setiap baris input yang diberikan?

-L 1adalah solusi sederhana tetapi tidak berfungsi jika ada file yang berisi spasi di dalamnya. Ini adalah fungsi utama -print0argumen find - untuk memisahkan argumen dengan karakter '\ 0' dan bukan spasi putih. Ini sebuah contoh:

echo "file with space.txt" | xargs -L 1 ls
ls: file: No such file or directory
ls: with: No such file or directory
ls: space.txt: No such file or directory

Solusi yang lebih baik adalah menggunakan truntuk mengonversi baris baru ke \0karakter null ( ), dan kemudian menggunakan xargs -0argumen. Ini sebuah contoh:

echo "file with space.txt" | tr '\n' '\0' | xargs -0 ls
file with space.txt

Jika Anda perlu membatasi jumlah panggilan, Anda dapat menggunakan -n 1argumen untuk melakukan satu panggilan ke program untuk setiap input:

echo "file with space.txt" | tr '\n' '\0' | xargs -0 -n 1 ls

Ini juga memungkinkan Anda untuk menyaring hasil pencarian sebelum mengubah jeda menjadi nol.

find . -name \*.xml | grep -v /target/ | tr '\n' '\0' | xargs -0 tar -cf xml.tar

1
Ada kesalahan sintaks pada blok kode kedua tr '\ n' '\ 0 \ => tr' \ n '' \ 0 ', saya mencoba untuk memperbaikinya tetapi "Suntingan harus minimal 6 karakter" (sepertinya ini bodoh seperti git yang menolak untuk melakukan karena perubahan saya kurang dari 6 karakter)
htaccess

1
Apa artinya ini: "Masalah lain dengan menggunakan -Ljuga adalah bahwa ia tidak mengizinkan beberapa argumen untuk setiap xargspanggilan perintah."?
Moberg

Saya telah meningkatkan jawaban saya untuk menghapus info tambahan @Moberg.
Gray

11

Alternatif lain ...

find /path -type f | while read ln; do echo "processing $ln"; done

9

Dua cara ini juga berfungsi, dan akan berfungsi untuk perintah lain yang tidak menggunakan find!

xargs -I '{}' rm '{}'
xargs -i rm '{}'

contoh use case:

find . -name "*.pyc" | xargs -i rm '{}'

akan menghapus semua file pyc di bawah direktori ini bahkan jika file pyc berisi spasi.


Ini mengeluarkan satu panggilan utilitas untuk setiap elemen yang tidak optimal.
Abu

7
find path -type f | xargs -L1 command 

itu yang kamu butuhkan.


4

Perintah berikut akan menemukan semua file (-type f) di /pathdan kemudian salin menggunakan cpke folder saat ini. Perhatikan penggunaan jika -I %untuk menentukan karakter placeholder di cpbaris perintah sehingga argumen dapat ditempatkan setelah nama file.

find /path -type f -print0 | xargs -0 -I % cp % .

Diuji dengan xargs (GNU findutils) 4.4.0


2

Anda dapat membatasi jumlah baris, atau argumen (jika ada spasi di antara setiap argumen) menggunakan flag --max-lines atau --max-args.

  -L max-lines
         Use at most max-lines nonblank input lines per command line.  Trailing blanks cause an input line to be logically continued on the next  input
         line.  Implies -x.

  --max-lines[=max-lines], -l[max-lines]
         Synonym  for  the -L option.  Unlike -L, the max-lines argument is optional.  If max-args is not specified, it defaults to one.  The -l option
         is deprecated since the POSIX standard specifies -L instead.

  --max-args=max-args, -n max-args
         Use at most max-args arguments per command line.  Fewer than max-args arguments will be used if the size (see  the  -s  option)  is  exceeded,
         unless the -x option is given, in which case xargs will exit.

0

Sepertinya saya tidak memiliki reputasi yang cukup untuk menambahkan komentar pada jawaban Tobia di atas , jadi saya menambahkan "jawaban" ini untuk membantu kita yang ingin bereksperimen dengan xargscara yang sama pada platform Windows.

Ini adalah file batch windows yang melakukan hal yang sama dengan script "show" Tobia yang cepat dikodekan:

@echo off
REM
REM  cool trick of using "set" to echo without new line
REM  (from:  http://www.psteiner.com/2012/05/windows-batch-echo-without-new-line.html)
REM
if "%~1" == "" (
    exit /b
)

<nul set /p=Args:  "%~1"
shift

:start
if not "%~1" == "" (
    <nul set /p=, "%~1"
    shift
    goto start
)
echo.

0

Jawaban @raemon tampaknya benar dengan "-0" bahkan dengan ruang dalam file.

Saya sedang mencoba perintah xargs dan saya menemukan bahwa "-0" berfungsi sempurna dengan "-L". bahkan spasi diperlakukan (jika input diakhiri nol). berikut ini adalah contohnya:

#touch "file with space"
#touch "file1"
#touch "file2"

Berikut ini akan membagi nulls dan menjalankan perintah pada setiap argumen dalam daftar:

 #find . -name 'file*' -print0 | xargs -0 -L1
./file with space
./file1
./file2

jadi -L1akan mengeksekusi argumen pada setiap karakter null yang dihentikan jika digunakan dengan "-0". Untuk melihat perbedaannya cobalah:

 #find . -name 'file*' -print0 | xargs -0 | xargs -L1
 ./file with space ./file1 ./file2

bahkan ini akan dijalankan sekali:

 #find . -name 'file*' -print0  | xargs -0  | xargs -0 -L1
./file with space ./file1 ./file2

Perintah akan dieksekusi sekali karena "-L" sekarang tidak terpecah pada byte nol. Anda harus memberikan "-0" dan "-L" agar berfungsi.


-3

Dalam contoh Anda, titik pemipaan output find ke xargs adalah perilaku standar opsi -exec find adalah untuk mengeksekusi perintah satu kali untuk setiap file yang ditemukan. Jika Anda menggunakan find, dan Anda menginginkan perilaku standarnya, maka jawabannya sederhana - jangan gunakan xargs untuk memulai.


Sebenarnya, apa yang bisa saya menyiratkan dari suntingan OP adalah bahwa input data tidak ada hubungannya dengan find, dan itulah sebabnya mereka tidak suka -execpilihan.
tzot

-3

jalankan tugas semut bersih-bersih pada setiap build.xml pada saat ini atau sub-folder.

find . -name 'build.xml' -exec ant -f {} clean-all \;

Tidak semua orang telah antmenginstal.
Gray
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.