“Xargs grep” ne yapar?


25

grepKomutu biliyorum ve fonksiyonlarını öğreniyorum xargs, bu yüzden komutun nasıl kullanılacağına dair bazı örnekler veren bu sayfayı okudum xargs.

Son örnek, örnek 10 ile kafam karıştı. "Xargs komutu, 'stdlib.h' dizesini içeren tüm dosyaları (find komutu tarafından sağlanan dosyalar arasında) bulmak için grep komutunu çalıştırır."

$ find . -name '*.c' | xargs grep 'stdlib.h'
./tgsthreads.c:#include
./valgrind.c:#include
./direntry.c:#include
./xvirus.c:#include
./temp.c:#include
...
...
...

Ancak, sadece kullanarak fark nedir

$ find . -name '*.c' | grep 'stdlib.h'

?

Açıkçası, hala tam olarak xargs'ın yaptığı şeyle mücadele ediyorum, bu yüzden herhangi bir yardım için teşekkür ederim!


2
Bu soru faydalı olabilir: XArgs ne zaman gereklidir?
The

Yanıtlar:


30
$ find . -name '*.c' | grep 'stdlib.h'

Bu çıkış (stdout) * ' finddan (stdin of) * grep 'stdlib.h' ' a metin olarak geçer (yani dosya isimleri metin olarak kabul edilir). grepher zamanki işini yapar ve bu metindeki eşleşen satırları bulur (kendileri kalıbı içeren herhangi bir dosya adı). Dosyaların içeriği asla okunmaz.

$ find . -name '*.c' | xargs grep 'stdlib.h'

Bu , her sonucun bir argüman olduğu bir komut grep 'stdlib.h' oluşturur find- bu, bulduğu her dosyanın içinde eşleşmeleri arayacaktır find( xargsstdin'i verilen komutlara argümanlara çevirdiği düşünülebilir) *

Find -type fkomutunuzda kullanın, yoksa grepeşleşen dizinler için hatalar alırsınız . Ayrıca, eğer dosya isimlerinde boşluklar varsa, xargskötü bir şekilde kötüleşir, bu yüzden boş ayırıcıyı ekleyerek -print0ve xargs -0daha güvenilir sonuçlar için kullanın:

find . -type f -name '*.c' -print0 | xargs -0 grep 'stdlib.h'

* bu ekstra açıklayıcı noktaları yorumunda @cat tarafından önerildiği gibi ekledi


2
o anahtar noktası (Eğer ihmal görünüyor çünkü) Eğer belirterek düşünebilirsiniz |boruları stdout'a grep en etmek Stdin grep argümanlarının aynı değildir ve kafa karıştırıcı sonuçlar verir.
kedi,

1
Veya GNU find'lerini kullanın find -name '*.c' -exec grep stdlib.h {} +. Asla xargs kullanmıyorum. Ayrıca şaşırmış hiç kimse, xargs'in grep $(find)oyuncu değiştirmeyi emretmek için benzer bir amaca hizmet etmediğinden bahsetti, ben de kendi cevabımı yazdım. Xargs'i daha az kısıtlı ve problemli komut yerine koyma olarak açıklamak doğal görünüyor.
Peter Cordes

Xargs kullandığım durumlardan biri, bulma sonucunda çok fazla dosyayı silmemdir. Eğer sadece -exec rm yaparsanız, her seferinde bir dosyada rm çalışacaktır, bu da çok verimsizdir. Xargs'a boru döşemek, hepsini bir kerede tek seferde yapacak. -N50 deyimi ile sınırlamak (bir seferde 50 yapın) komut satırının taşmasını engelleyebilir (birçok dosyada sorun olabilir).
05:

1
@lsd: Neden find -deletebu özel durum için değil ? Veya rmGNU bulmanız dışında komutlar için, her biri için ayrı komut çalıştırma davranışı -exec some_command {} +yerine, xargs gibi gruplar halinde gruplar oluşturur \;.
Peter Cordes

@lsd findher bir dosya için yalnızca çalıştırırsa ve -exec command \;Her ikisi de kullanıyorsa xargsve -exec command \+komutu sistem tarafından izin verilen maksimum sayıda argümanla çağırırsa komut çalıştırır. Başka bir deyişle, eşdeğerdirler
Sergiy Kolodyazhnyy

6

xargs standart girişini alır ve komut satırı argümanlarına dönüştürür.

find . -name '*.c' | xargs grep 'stdlib.h' çok benzer

grep 'stdlib.h' $(find . -name '*.c')  # UNSAFE, DON'T USE

Ayrıca, dosya listesi tek bir komut satırı için çok uzun olmadığı sürece aynı sonuçları verecektir. (Linux, tek bir komut satırında megabayt metinleri destekler, bu nedenle genellikle xargs gerekmez.)


Ancak bunların ikisi de emilir, çünkü dosya adlarınız boşluk içeriyorsa kırılırlar . Bunun yerine, find -print0 | xargs -0çalışır, ancak çalışır

find . -name '*.c' -exec grep 'stdlib.h' {} +

Bu hiçbir zaman dosya adlarını hiçbir yerde findyayınlamıyor : onları büyük bir komut satırına ayırıyor ve grepdoğrudan çalışıyor .

\;Bunun yerine +, daha yavaş olan her dosya için ayrı ayrı grep çalışır. Yapma bunu. Ancak +bir GNU uzantısıdır, bu yüzden xargsGNU’nun bulduklarını varsayamazsanız, bunu verimli bir şekilde yapmanız gerekir .


Eğer bırakırsanız xargs, find | grepo dosya adları listesine karşı desen eşleştirme yapar findbaskılar.

Yani bu noktada, siz de yapabilirsiniz find -name stdlib.h. Tabii ki, ile -name '*.c' -name stdlib.h, herhangi bir çıktı elde edemezsiniz, çünkü bu kalıplar hem eşleşemez, hem de bulgunun varsayılan davranışı AND ile birliktedir.

Yedek lesssürecinde herhangi bir noktada boru hattının herhangi bir kısmı ne ürettiği görmek için.


Daha fazla okuma: http://mywiki.wooledge.org/BashFAQ harika şeyler içeriyor.


1
GNU xargs ayrıca -dayracı da ayarlamalıdır, böylece -d'\n'bir dosyadaki dosya adlarının bir listesini işliyorsanız yararlı olabilecek yeni satırla ayrılmış bir listeyi işlemek için kullanabilirsiniz .
içlerindeki yeni çizgiler

@ilkkachu: evet, dosya adlarındaki yeni satırlar çoğu betiği kırdıkları için boşluklardan çok daha nadirdir. myfunc(){ local IFS=$'\n'; fgrep stdlib.h` $ (find) ; }aynı etkiyle çalışır. Veya bir astar olarak, bir (IFS=...; cmd...)alt kabuk ayrıca kaydetmek / geri yüklemek zorunda kalmadan IFS'deki değişikliği içerecek şekilde çalışır.
Peter Cordes

@PeterCordes Lütfen bir command $( find )şeyler yapmayın . Boşluklu ve özel karakterli problemli dosya isimleri bu tip bir şeyi bozabilir. En azından çifte komut değiştirme işlemi.
Sergiy Kolodyazhnyy

@SergiyKolodyazhnyy: Aslında bunu yapmayı öneriyorum gibi göründüğünü belirttiğiniz için teşekkürler. Kaynayan insanlar, bir sonraki bölümü okumak yerine kopyalayıp / yapıştırmış olabilir. Bunu ele almak için güncellendi.
Peter Cordes

@SergiyKolodyazhnyy: Yoksa yorumuma cevap mı veriyorsun? Belirlediğimden IFS, kullanmaya eşdeğer olduğuna dikkat edin xargs '-d\n'. Glob genişlemesi ve kabuk meta karakter işleme işlemi, komut değişikliğinin etkilerinden önce gerçekleşir, bu nedenle $()veya içeren dosya adlarında bile güvenli olduğunu düşünüyorum >. Komut ikameinde kelime bölmeyi kullanmanın, dosya isimleri hakkında bir şeyler bildiğiniz bir kerelik etkileşimli kullanım dışında iyi bir uygulama olmadığını kabul etti. Ancak command "$(find)"yalnızca tam olarak 1 dosya adı oluşturmasını beklerseniz kullanışlıdır ...
Peter Cordes

5

Genelde, bir komuttan diğerine ( xargssembolle |) bir şey iletmek istediğiniz durumlarda ( Command1 | Command2) kullanılır, ancak ilk komutun çıktısı, ikinci komutun girişi olarak doğru şekilde alınmaz.

Bu genellikle, ikinci komut Standard In (stdin) üzerinden veri girişini doğru bir şekilde işlemediğinde (örneğin: Giriş olarak çoklu satırlar, satırların kurulum şekli, giriş olarak kullanılan karakterler, giriş olarak birden fazla parametre, alınan veri türü gibi) gerçekleşir giriş, vb ..). Size hızlı bir örnek vermek için aşağıdakileri test edin:

Örnek 1:

ls | echo - Bu o zamandan beri hiçbir şey yapmayacak echo aldığı girişi nasıl kullanacağını bilmediğinden yapmaz. Şimdi bu durumda kullanırsak xargs, girişi doğru olarak kullanılabilecek şekilde işler echo(örneğin: Tek bir bilgi satırı olarak)

ls | xargs echo- Bu, tüm bilgileri lstek bir satırda verir.

Örnek 2:

Diyelim ki go adlı bir klasörde birden fazla goLang dosyası var. Onlara böyle bir şeyle bakardım:

find go -name *.go -type f | echo - Ama orada boru sembolü ve echo sonunda , işe yaramaz.

find go -name *.go -type f | xargs echo - İşte sayesinde işe yarar xargs yarar ancak findkomuttan gelen her bir yanıtı tek bir satırda istiyorsam aşağıdakileri yapardım:

find go -name *.go -type f | xargs -0 echo - Bu durumda, aynı çıktı find ile gösterilir echo.

Gibi komutlar cp, echo, rm, lessve girişi ele almak için daha iyi bir yol gerektiren diğerleri ile birlikte kullanıldığında bir fayda olsun xargs.


4

xargs otomatik olarak bir dosya listesine dayalı (genellikle) komut satırı argümanlarını otomatik olarak üretmek için kullanılır.

Yani followoing xargskomutunu kullanmanın bazı alternatiflerini göz önünde bulundurarak :

find . -name '*.c' -print0 | xargs -0 grep 'stdlib.h'

İlk başta diğer cevaplarda belirtilmeyen diğer seçenekler yerine onu kullanmanın birkaç nedeni vardır:

  1. find . -name '*.c' -exec grep 'stdlib.h' {}\; bir üretecek grepHer dosya için işlem - bu genellikle kötü bir uygulama olarak kabul edilir ve çok sayıda dosya bulunursa sisteme büyük bir yük getirebilir.
  2. Çok fazla dosya varsa, grep 'stdlib.h' $(find . -name '*.c')komutun başarısız olması muhtemeldir çünkü $(...)işlemin çıktısı kabuğun maksimum komut satırı uzunluğunu aşacaktır.

Diğer cevaplarda da belirtildiği gibi , bu senaryoda bu argümanı ve xargs -print0argümanını kullanmanın nedeni , belirli karakterlere sahip dosya adlarının (örneğin; tırnak, boşluk veya hatta yeni satırlar) hala doğru bir şekilde ele alınmasıdır.find-0

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.