stdin / stdout yönlendirmesi olan xargs


21

Koşmak istiyorum:

./a.out < x.dat > x.ans

Her * için .dat dizin dosya A .

Elbette, bash / python / whatsoever betiği ile yapılabilir, ancak seksi bir harf yazmayı seviyorum. Ulaşabileceğim tek şey (hala herhangi bir stdout olmadan):

ls A/*.dat | xargs -I file -a file ./a.out

Fakat xargs içindeki -a , yerine-str 'dosyasını' anlamıyor.

Yardımın için teşekkürler.


Buradaki diğer iyi cevaplara ek olarak, GNU xargs'ın -a seçeneğini kullanabilirsiniz.
James Youngman,

Yanıtlar:


30

Öncelikle, çıktıyı bir dosya listesi olarak kullanmayınls . Kabuk genişletme kullanın veya find. Ls + xargs yanlış kullanımının olası sonuçları ve uygun xargskullanım örneği için aşağıya bakın .

1. basit yolu: için döngü

Yalnızca altındaki dosyaları işlemek istiyorsanız A/, basit bir fordöngü yeterli olacaktır:

for file in A/*.dat; do ./a.out < "$file" > "${file%.dat}.ans"; done

2. pre1 Neden olmasın   ls | xargs ?

Burada kullanırsanız dönüşebilir ne kadar kötü bir örnek lsile xargsbu iş için. Aşağıdaki senaryoyu inceleyin:

  • ilk olarak, bazı boş dosyalar oluşturalım:

    $ touch A/mypreciousfile.dat\ with\ junk\ at\ the\ end.dat
    $ touch A/mypreciousfile.dat
    $ touch A/mypreciousfile.dat.ans
    
  • dosyaları görün ve hiçbir şey içermediğini görün:

    $ ls -1 A/
    mypreciousfile.dat
    mypreciousfile.dat with junk at the end.dat
    mypreciousfile.dat.ans
    
    $ cat A/*
    
  • kullanarak sihirli bir komut çalıştırın xargs:

    $ ls A/*.dat | xargs -I file sh -c "echo TRICKED > file.ans"
    
  • sonuç:

    $ cat A/mypreciousfile.dat
    TRICKED with junk at the end.dat.ans
    
    $ cat A/mypreciousfile.dat.ans
    TRICKED
    

Sadece başardınız Yani hem üzerine yazmak mypreciousfile.datve mypreciousfile.dat.ans. Bu dosyalarda herhangi bir içerik olsaydı, silinirdi.


2. kullanma   xargs : ile uygun şekilde  find 

Eğer kullanmakta ısrar etmek istiyorsanız xargs, şunu kullanın -0(boş bırakılmış isimler):

find A/ -name "*.dat" -type f -print0 | xargs -0 -I file sh -c './a.out < "file" > "file.ans"'

İki şeye dikkat edin:

  1. bu şekilde .dat.ansbiten dosyalar oluşturacaksınız ;
  2. Bu , eğer bir dosya adı bir alıntı işareti ( ") içeriyorsa kırılır .

Her iki sorun da farklı kabuk çağırma yöntemleriyle çözülebilir:

find A/ -name "*.dat" -type f -print0 | xargs -0 -L 1 bash -c './a.out < "$0" > "${0%dat}ans"'

3. içinde yapılan tüm find ... -exec

 find A/ -name "*.dat" -type f -exec sh -c './a.out < "{}" > "{}.ans"' \;

Bu, yine .dat.ansdosyalar üretir ve dosya adları içeriyorsa bozulur ". Bunun bashiçin, çağrılma biçimini kullanın ve değiştirin:

 find A/ -name "*.dat" -type f -exec bash -c './a.out < "$0" > "${0%dat}ans"' {} \;

Ls çıktısını ayrıştırmamaya değinmek için +1.
rahmu

2
Seçenek 2, dosya
isimleri

2
Çok iyi nokta, teşekkürler! Buna göre güncelleyeceğim.
rozcietrzewiacz 7:11

Sadece eğer, söz istiyorum zshdeğildir, bu da gerek (dosya adı vb "boş alanların), kabuk olarak kullanılır (ve SH_WORD_SPLIT ayarlanmamış) Tüm pis özel durumları önemsiz. for file in A/*.dat; do ./a.out < $file > ${file%.dat}.ans ; doneEserlerini her durumda.
jofel

-1 çünkü stdin ile xargs yapmayı anlamaya çalışıyorum ve sorumun dosyalar ile ilgisi yok find.
Mehrdad

2

Böyle bir şey yapmayı deneyin (sözdizimi kullandığınız kabuğa bağlı olarak biraz değişebilir):

$ for i in $(find A/ -name \*.dat); do ./a.out < ${i} > ${i%.dat}.ans; done


Bu işe yaramaz. somefile.dat.datTüm çıktıları tek bir dosyaya yönlendirmeye ve yönlendirmeye çalışır .
rozcietrzewiacz

Haklısın. Çözümü düzeltmek için düzenlemiştim.
rahmu

Tamam - Neredeyse iyi :) Sadece somefile.dat.ansçıktı şeyler çok hoş görünmüyordu.
rozcietrzewiacz

1
Düzenlenen! '%' Hakkında bir şey bilmiyordum. Bir cazibe gibi çalışır, bahşiş için teşekkürler.
rahmu

1
Bir eklemek -type filegüzel olurdu (olamaz < directory) ve bu sıra dışı-dosya adı-periyi hüzünlendirir.
Mat

2

Basit kalıplar için for döngüsü uygundur:

for file in A/*.dat; do
    ./a.out < "${file}" > "${file%.dat}.ans" # Never forget the QUOTES!
done

Dosyaları listelemek için başka bir yardımcı programa ihtiyaç duyduğunuz daha karmaşık durumlar için (zsh veya bash 4 nadiren bulmanız gereken yeterince güçlü desenlere sahiptir, ancak POSIX kabuğu içinde kalmak veya kısa çizgi gibi hızlı kabuk kullanmak istiyorsanız, herhangi bir şey bulmanız gerekir. önemsiz olmayan), okurken en uygun olanı:

find A -name '*.dat' -print | while IFS= read -r file; do
   ./a.out < "${file}" > "${file%.dat}.ans" # Never forget the QUOTES!
done

Bu boşlukları işleyecektir, çünkü okuma (varsayılan olarak) satır yönelimlidir. Yeni satırları işlemeyecek ve ters eğik çizgileri işlemeyecek, çünkü varsayılan olarak kaçış dizilerini yorumlar (bu aslında yeni bir satırda geçmenize olanak tanır, ancak bu biçimi bulamaz). Birçok merminin -0okuma seçeneği vardır, böylece tüm karakterleri kullanabilirsiniz. Fakat maalesef POSIX değildir.



1

Bence en azından xargs'ta bir kabuk kullanımına ihtiyacınız var:

ls A/*.dat | xargs -I file sh -c "./a.out < file > file.ans"

Düzenleme: Dosya adları boşluk içerdiğinde bu yaklaşımın işe yaramadığına dikkat edilmelidir. Çalışamazsın. Xargs'in boşlukları doğru bir şekilde anlamasını sağlamak için -0 ve -arg komutlarını -0 kullanmış olsanız bile, -c kabuk çağrısı bunlara çarpacaktır. Ancak, OP açıkça bir xargs çözümü istedi ve bu benim ortaya çıktığım en iyi xargs çözümü. Dosya adlarındaki boşluk bir sorunsa, find -exec veya bir kabuk döngüsü kullanın.



@rozcietrzewiacz: Genel olarak, elbette, ancak xargs voodoo yapmaya çalışan birinin bunu bildiğini varsayıyorum. Bu dosya isimleri başka bir kabuk genişlemesine maruz kaldıklarından, yine de iyi davranılmaları gerekir.
thiton

1
Kabuk genişlemesi konusunda yanılıyorsunuz. lskaçan ve olmayan boşluklar gibi çıkışlar şeyler olduğunu sorundur.
rozcietrzewiacz

@ rozcietrzewiacz: Evet, bu sorunu anlıyorum. Şimdi, bu boşluklardan düzgün bir şekilde kaçabileceğinizi ve onları xargs içine alacağınızı varsayalım: sh'in -c dizesinde değiştirilir, sh -c dizgisini belirtir ve her şey bozulur.
thiton

Evet. Şimdi görüyorsunuz, ayrıştırma lsiyi değil. Ama xargs olabilir güvenle Bul ile kullanılabilir - cevabım öneri yok 2 bkz.
rozcietrzewiacz

1

Karmaşık olmaya gerek yok. Bunu bir fordöngü ile yapabilirsin :

for file in A/*.dat; do
  ./a.out < "$file" >"${file%.dat}.ans"
done

${file%.dat}.ansBit kaldıracak .datiçinde dosya ismi dosya eki $fileve bunun yerine eklemek .anssonuna kadar.

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.