Xargs komutunun her girdi satırı için komutu bir kez çalıştırmasını sağlama


341

Xargs komutunun verilen her girdi satırı için tam olarak bir kez çalıştırılmasını nasıl sağlayabilirim? Varsayılan davranışı, satırları yığınlamak ve komutu bir kez yürütmek ve her örneğe birden çok satır iletmektir.

Gönderen http://en.wikipedia.org/wiki/Xargs :

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

Bu örnekte find, xargs girişini uzun bir dosya adı listesiyle besler. xargs daha sonra bu listeyi alt listelere böler ve her alt liste için bir kez rm çağırır. Bu, işlevsel olarak eşdeğer versiyondan daha verimlidir:

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

Bulmanın "exec" bayrağı olduğunu biliyorum. Sadece başka bir kaynaktan açıklayıcı bir örnek alıntılıyorum.


4
Sağladığınız örnekte, find /path -type f -deletedaha da verimli olurdu :)
tzot

xargs kullanmamaya çalışın ...
Naib

6
OP, bu sorunun çok eski olduğunu biliyorum, ama yine de Google ve IMHO'da geliyor, kabul edilen cevap yanlış. Daha uzun cevabımı aşağıya bakın.
Tobia

Lütfen kabulünüzü @ Tobia'nın cevabına çevirmeyi düşünün. Kabul edilen cevap, adlardaki boşlukları işlemez ve xargs'ın ana özelliklerinden biri olan xargs komutunda birden çok bağımsız değişkene izin vermez.
Gri

Yanıtlar:


394

Aşağıdakiler yalnızca girişinizde boşluk yoksa çalışır:

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

man sayfasından:

-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
Benim için verdiğiniz gibi xargs -n 1"argüman listesi çok uzun" gösterdi.
Wernight

19
Eğer MAX-LINES1'e o öntanımlıdır, dolayısıyla xargs -lyeterli. Bkz info xargs.
Thor

3
@Wernight: "-n1", girdi satırı başına 1 çağrı vermez. belki giriş hattınız çok uzundu. demo: echo "foo bar" | xargs -n1 echo. bu yüzden 'ls' gibi şeyleri koyarsanız, boşlukları iyi idare etmez.
gatoatigrado

8
Bu yanlış. -L 1orijinal soruya cevap vermez ve -n 1sadece olası yorumlardan birinde yapar. Uzun cevabımı aşağıya bakın.
Tobia

2
@ Tobia: Bu, özellikle girdi satırlarıyla ilgili orijinal soruyu cevaplar. Aynen -L 1öyle. Bana göre OP, varsayılan parçalama davranışından kaçınmaya çalışıyor gibi görünüyordu ve bu kabul edildiğinden beri haklı olduğumu varsayıyorum. Cevabınız, parçalama davranışını da istediğiniz biraz farklı bir kullanım senaryosunu ele alıyor.
Draemon

207

Bana öyle geliyor ki, bu sayfadaki mevcut cevaplar yanlış (doğru olarak işaretlenmiş olanlar dahil). Bu, sorunun belirsiz bir şekilde ifade edildiği gerçeğinden kaynaklanmaktadır.

Özet:   Komutun tamamını (yeni satır olmadan) komuta tek bir argüman olarak ileterek "verilen her girdi satırı için tam olarak bir kez" komutunu yürütmek istiyorsanız , bu UNIX uyumlu en iyi yoldur:

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

GNU xargs, ortadan kaldırmanıza izin veren yararlı uzantılara sahip olabilir veya olmayabilir tr, ancak OS X ve diğer UNIX sistemlerinde mevcut değildir.

Şimdi uzun bir açıklama için…


Xargs kullanırken dikkate alınması gereken iki sorun vardır:

  1. girdiyi "argümanlara" nasıl böler; ve
  2. child komutunu bir seferde kaç argümanın geçeceğini.

Xargs'ın davranışını test etmek için, kaç kez yürütüldüğünü ve kaç argümanla olduğunu gösteren bir yardımcı programa ihtiyacımız var. Bunu yapmak için standart bir yardımcı program olup olmadığını bilmiyorum, ama bash kolayca kodlayabilirsiniz:

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

showGeçerli dizininizdeki gibi kaydettiğinizi ve çalıştırılabilir hale getirdiğinizi varsayarsak , şöyle çalışır:

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

Şimdi, eğer asıl soru gerçekten 2. nokta ile ilgili ise (sanırım, birkaç kez okuduktan sonra) ve bu şekilde okunmalıdır (kalın değişiklikler):

Xargs komutunun verilen her bir giriş argümanı için tam olarak bir kez çalışmasını nasıl sağlayabilirim ? Varsayılan davranışı, girdiyi bağımsız değişkenlere yığınlamak ve komutu her örneğe birden çok bağımsız değişken ileterek mümkün olduğunca az yürütmektir .

o zaman cevap -n 1.

Girdiyi boşluk içine ayıran ve komutu mümkün olduğunca az kez çağıran xargs'ın varsayılan davranışını karşılaştıralım:

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

ve davranışları -n 1:

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

Öte yandan, asıl soru nokta 1 ile ilgiliyse. Giriş bölme ve bu şekilde okunacaksa (buraya gelen birçok kişi durumun böyle olduğunu düşünüyor veya iki konuyu karıştırıyor gibi görünüyor):

Nasıl Xargs komutunu çalıştırmak yapabilirsiniz ile tam bir argüman verilen girişin her satırı için? Varsayılan davranışı, boşluk çevresindeki çizgileri yığınlamaktır .

o zaman cevap daha incedir.

Biri bunun -L 1yardımcı olabileceğini düşünür , ancak argüman ayrışmasını değiştirmediği ortaya çıkıyor. Komutu, her bir girdi satırı için yalnızca bir kez, o girdi satırında olduğu kadar çok argümanla yürütür:

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

Sadece bu değil, bir satır boşlukla bitiyorsa, bir sonrakine eklenir:

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

Açıkçası, -Lxargs'ın girdiyi bağımsız değişkenlere ayırma biçimini değiştirmekle ilgili değildir.

Bunu çapraz platform biçiminde (GNU uzantıları hariç) yapan tek argüman -0, girdiyi NUL baytlarına böler.

O zaman, bu sadece yeni satırları NUL'a çeviri yardımı ile yapar tr:

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

Şimdi argüman ayrıştırma, sondaki boşluk dahil olmak üzere iyi görünüyor.

Son olarak, bu tekniği birleştirirseniz -n 1, giriş satırınız başına tam olarak bir komut yürütme elde edersiniz, hangi girdiniz varsa, orijinal soruya bakmanın başka bir yolu olabilir (muhtemelen en sezgisel, başlık verilir):

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

Bu daha iyi bir cevap gibi görünüyor. Ancak, hala -L ve -n arasındaki farkın ne olduğunu tam olarak anlamıyorum ... biraz daha açıklayabilir misiniz?
olala

5
@olala -Lkomutu her giriş satırı için bir kez yürütür (ancak bir satırın sonundaki boşluk bir sonraki satıra birleştirir ve satır yine de boşluğa göre bağımsız değişkenlere ayrılır); while -nkomutu her girdi argümanı için bir kez yürütür. ->Çıktı örneklerindeki sayısını sayarsanız , komut dosyasının ./showyürütme sayısıdır .
Tobia

anlıyorum! bir satırın sonundaki boşluğun bir sonraki satıra katıldığını fark etmedi. Teşekkürler!
olala

4
GNU xargs, bunu yapmanıza izin veren yararlı uzantılara sahip olabilir veya olmayabilirtr Bu çok yararlı bir uzantıya sahiptir; from xargs --help-- d, --delimiter = Giriş akışındaki CHARACTER öğeleri boşlukla değil CHARACTER ile ayrılır; teklif ve ters eğik çizgi işleme ve mantıksal EOF işlemeyi devre dışı bırakır
Piotr Dobrogost

Bu cevap konusunda karışık görünüyor -L. -Lkomut dosyasını satır başına kaç kez yürütmek gerektiğini söylemez, aynı anda kaç satır giriş verisi tüketileceğini belirtir.
Moberg

22

Komutu gelen her satır (yani sonuç) için çalıştırmak istiyorsanız find, o zaman neye ihtiyacınız var xargs?

Deneyin:

find komutunu yolla-type f -exec {} \;

burada değişmezi {}dosya adı ile değiştirir ve değişmez özel komutun burada sona erdiğini bilmek \;için gereklidir find.

DÜZENLE:

(sorunuzun düzenlendikten sonra bildiğinizi açıklığa kavuşturun -exec)

Gönderen man xargs:

-L maksimum satır Komut satırı başına
en fazla maksimum satır boşluksuz giriş satırı kullanın. Sondaki boşluklar, bir giriş satırının bir sonraki giriş satırında mantıksal olarak devam etmesine neden olur. -X anlamına gelir.

Boşluklarla biten dosya adlarının aşağıdakileri kullanırsanız soruna neden olabileceğini unutmayın 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

Bu yüzden -execseçeneği umursamıyorsanız , daha iyi kullanırsınız -print0ve -0:

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

18

Xargs komutunun verilen her girdi satırı için tam olarak bir kez çalıştırılmasını nasıl sağlayabilirim?

-L 1basit bir çözümdür, ancak dosyalardan herhangi birinde boşluklar varsa çalışmaz. Bu, find -print0argümanının önemli bir işlevidir - bağımsız değişkenleri boşluk yerine '\ 0' karakteriyle ayırmaktır. İşte bir örnek:

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

Daha iyi bir çözüm, tryeni satırları null ( \0) karakterlerine dönüştürmek ve sonra xargs -0argümanı kullanmaktır . İşte bir örnek:

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

Daha sonra çağrı sayısını sınırlamanız gerekiyorsa, -n 1her giriş için programa bir çağrı yapmak üzere bağımsız değişkeni kullanabilirsiniz :

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

Bu , araları boş değerlere dönüştürmeden önce bulmanın çıktısını filtrelemenize de olanak tanır .

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

1
Tr '\ n' '\ 0 \ => tr' \ n '' \ 0 'ikinci kod bloğunda bir sözdizimi hatası var, bunu düzeltmeye çalıştım ama "Düzenlemeler en az 6 karakter olmalı" (bu gibi görünüyor) git benim değişiklik 6 karakter daha az olduğu için taahhüt reddeden aptal)
htaccess

1
Bunun anlamı: "Kullanmayla ilgili başka bir sorun -Lda her xargskomut çağrısı için birden fazla argümana izin vermemesidir ."
Moberg

@Moberg'deki bu gereksiz bilgiyi kaldırmak için cevabımı geliştirdim.
Gri

11

Başka bir alternatif ...

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

9

Bu iki yol da işe yarar ve find kullanmayan diğer komutlar için de çalışır.

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

örnek kullanım örneği:

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

pyc dosyalarında boşluk olsa bile bu dizin altındaki tüm pyc dosyalarını silecektir.


Bu, optimal olmayan her öğe için bir yardımcı çağrı verir.
Gri

7
find path -type f | xargs -L1 command 

tüm ihtiyacın olan.


4

Aşağıdaki komut, içindeki tüm dosyaları (-type f) bulur ve geçerli klasöre /pathkullanarak kopyalar cp. Bağımsız değişkenlerin dosya adından sonra yerleştirilebilmesi -I %için cpkomut satırında bir yer tutucu karakter belirtilecekse, use'a dikkat edin.

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

Xargs (GNU findutils) ile test edilmiştir 4.4.0


2

Sırasıyla --max-lines veya --max-args bayraklarını kullanarak satır veya bağımsız değişken sayısını (her bağımsız değişken arasında boşluk varsa) sınırlandırabilirsiniz.

  -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

Görünüşe göre yukarıdaki Tobia'nın cevabına bir yorum eklemek için yeterli üne sahip değilim, bu yüzden xargsWindows platformlarında aynı şekilde denemek isteyenlere yardımcı olmak için bu "cevabı" ekliyorum .

İşte Tobia'nın hızlı kodlanmış "göster" betiği ile aynı şeyi yapan bir Windows toplu iş dosyası:

@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

@Draemon yanıtları dosyada boşluk olsa bile "-0" ile doğru görünüyor.

Ben xargs komutunu deniyordum ve "-0" "-L" ile mükemmel çalışır bulundu. boşluklar bile işlenir (giriş boş bırakılmışsa). aşağıdakiler bir örnektir:

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

Aşağıdakiler null'ları böler ve listedeki her bağımsız değişken için komutu yürütür:

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

böylece -L1, "-0" ile birlikte kullanılırsa her boş sonlandırılmış karakterde argümanı yürütür. Farkı görmek için şunu deneyin:

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

bu bile bir kez yürütülür:

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

"-L" artık boş bayta bölünmediği için komut bir kez yürütülür. çalışmak için hem "-0" hem de "-L" sağlamanız gerekir.


-3

Örneğinizde, find çıkışının xargs'a aktarılması noktası, find'ın -exec seçeneğinin standart davranışının, bulunan her dosya için komutu bir kez yürütmek olmasıdır. Find kullanıyorsanız ve standart davranışını istiyorsanız, cevap basittir - başlamak için xargs kullanmayın.


Aslında, OP'nin düzenlemelerinden ima edebileceğim şey, giriş verilerinin hiçbir ilgisi olmadığı findve bu yüzden -execseçeneği tercih etmemeleri .
09:11

-3

geçerli veya alt klasördeki her build.xml dosyasında ant-clean komutunu yürütün.

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

Herkes antyüklemedi.
Gri
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.