Argüman olarak bulunan dosyaları argüman olarak nasıl aktarabilirim?


9

İlk önemsiz ama uygulanamaz cevapları kapalı kesim için: Ben ne kullanabilirsiniz find+ xargshile ne de (gibi varyasyonlarını findile -execben görüşme başına kaç tür ifadeleri kullanmak gerekir çünkü). Sonunda buna geri döneceğim.


Şimdi daha iyi bir örnek için şunu düşünelim:

$ find -L some/dir -name \*.abc | sort
some/dir/1.abc
some/dir/2.abc
some/dir/a space.abc

Bunları nasıl argüman olarak aktarabilirim program?

Sadece yapmak hile yapmaz

$ ./program $(find -L some/dir -name \*.abc | sort)

programaşağıdaki argümanları aldığından beri başarısız olur:

[0]: ./program
[1]: some/dir/1.abc
[2]: some/dir/2.abc
[3]: some/dir/a
[4]: space.abc

Görülebileceği gibi, uzaylı yol ayrıldı ve programonu iki farklı argüman olarak görüyor.

Çalışana kadar alıntı yap

Kendim gibi acemi kullanıcılar, bu tür sorunlarla karşılaştıklarında, nihayet işe yarayana kadar rastgele tırnak ekleme eğilimindedir - sadece burada yardımcı görünmüyor…

"$(…)"

$ ./program "$(find -L some/dir -name \*.abc | sort)"
[0]: ./program
[1]: some/dir/1.abc
some/dir/2.abc
some/dir/a space.abc

Tırnaklar kelime bölünmesini engellediğinden, tüm dosyalar tek bir argüman olarak iletilir.

Bireysel yolları alıntılama

Gelecek vaat eden bir yaklaşım:

$ ./program $(find -L some/dir -name \*.abc -printf '"%p"\n' | sort)
[1]: "some/dir/1.abc"
[2]: "some/dir/2.abc"
[3]: "some/dir/a
[4]: space.abc"

Alıntılar var, tabi. Ama artık yorumlanmıyorlar. Onlar sadece dizelerin bir parçası. Yani sadece kelime bölünmesini engellemediler, aynı zamanda argümanlara da girdiler!

IFS'yi değiştirin

Sonra ile oynamayı denedim IFS. Ben tercih ediyorum findile -print0ve sortile -zyine - onlar "kablolu yolları" kendileri üzerinde hiçbir sorunları olacak böylece. Öyleyse neden kelimeyi nullkaraktere ayırmaya zorlamıyorsunuz ve hepsine sahip misiniz?

$ ./program $(IFS=$'\0' find -L some/dir -name \*.abc -print0 | sort -z)
[0]: ./program
[1]: some/dir/1.abcsome/dir/2.abcsome/dir/a
[2]: space.abc

Bu yüzden hala uzaya ayrılıyor ve üzerinde bölünmüyor null.

IFSÖdevi hem $(…)yukarıda (yukarıda gösterildiği gibi) hem de daha önce yerleştirmeye çalıştım ./program. Ayrıca ben gibi diğer sözdizimi çalıştı \0, \x0, \x00hem alıntı 've "olan ve olmayan yanı sıra $. Bunların hiçbiri fark yaratmadı ...


Ve burada fikirlerim kalmadı. Birkaç şey daha denedim ama hepsi listelenen aynı sorunları koşmak gibi görünüyordu.

Başka ne yapabilirdim? Hiç yapılabilir mi?

Tabii, programkalıpları kabul edebilir ve kendi aramaları yapabilirim. Ancak belirli bir sözdizimine sabitlerken çok fazla çift çalışma var. ( grepÖrneğin, dosyaları bir dosya ile sağlamaya ne dersiniz ?).

Ayrıca programyolların listesini içeren bir dosyayı kabul edebilirim. Sonra kolayca findbazı geçici dosyaya ifade dökümü ve sadece bu dosyaya yol sağlayabilir. Bu, doğrudan yollar boyunca desteklenebilir, böylece kullanıcı sadece basit bir yola sahipse, ara dosya olmadan sağlanabilir. Ancak bu hoş görünmüyor - bir kişinin ekstra dosyalar oluşturması ve bunlarla ilgilenmesi gerekiyor, gerekli ekstra uygulamadan bahsetmiyoruz. (Ancak, artı tarafta, bağımsız değişken olarak dosya sayısının komut satırı uzunluğu ile ilgili sorunlara neden olduğu durumlar için bir kurtarma olabilir ...)


Sonunda, tekrar hatırlatmama izin verin ki find+ xargs(ve benzer) numaralar benim durumumda işe yaramayacak. Açıklama basitliği için sadece bir argüman gösteriyorum. Ama benim gerçek durumum daha çok şöyle görünüyor:

$ ABC_FILES=$(find -L some/dir -name \*.abc | sort)
$ XYZ_FILES=$(find -L other/dir -name \*.xyz | sort)
$ ./program --abc-files $ABC_FILES --xyz-files $XYZ_FILES

Yani xargsbir arama yapmak hala diğeri ile başa çıkmak için bana bırakıyor…

Yanıtlar:


13

Diziler kullanın.

Dosya adlarınızdaki yeni satırların olasılığını ele almanız gerekmiyorsa,

mapfile -t ABC_FILES < <(find -L some/dir -name \*.abc | sort)
mapfile -t XYZ_FILES < <(find -L other/dir -name \*.xyz | sort)

sonra

./program --abc-files "${ABC_FILES[@]}" --xyz-files "${XYZ_FILES[@]}"

Eğer varsa yapmak dosya içinde sap satırsonlarına ihtiyacını ve bash> = 4.4, kullanabileceğiniz var -print0ve -d ''için dizi inşaatı sırasında isimlerini boş-sonlandırmak:

mapfile -td '' ABC_FILES < <(find -L some/dir -name \*.abc -print0 | sort -z)

(ve benzer şekilde XYZ_FILES). Eğer varsa yok daha yeni bash, o zaman diziler örneğin üzere dosya eklemek için boş sonlandırılmış okuma döngü kullanabilirsiniz

ABC_FILES=()
while IFS= read -rd '' f; do ABC_FILES+=( "$f" ); done < <(find -L some/dir -name \*.abc -print0 | sort -z)

Mükemmel! Dizileri düşünüyordum. Ama bir şekilde bunun hakkında hiçbir şey bulamadım mapfile(ya da eşanlamlısı readarray). Ama işe yarıyor!
Adam Badura

Yine de biraz geliştirebilirsin. Bir whiledöngü ile Bash <4.4 sürümü (ki ben var ...) dizi temizlemez. Bu, hiçbir dosya bulunamazsa dizinin tanımsız olduğu anlamına gelir. Önceden tanımlanmışsa, yeni dosyalar eklenecektir (eskilerini değiştirmek yerine). Daha declare -a ABC_FILES='()';önce eklemenin whilehile yaptığı anlaşılıyor . ( ABC_FILES='()';
Eklerken

Ayrıca < <burada ne anlama geliyor? İle aynı <<mı? Ben <<sözdizimi hatası ("beklenmedik jeton` `'') olarak değiştirmek gibi düşünmüyorum.Öyleyse ne olduğunu ve nasıl çalışır?
Adam Badura

Başka bir gelişme (benim özel kullanımım boyunca) başka bir dizi oluşturmaktır. Yani bunlara sahibiz ABC_FILES. Bu iyi. Ancak , boşsa hangisinin boş ABS_ARGSolduğunu ABC_FILES, yoksa bir dizi olduğunu yapmak yararlıdır ('--abc-files' "${ABC_FILES[@]}"). Bu şekilde daha sonra şu şekilde kullanabilirim: ./program "${ABC_ARGS[@]}" "${XYZ_ARGS[@]}"ve gruplardan hangisi (varsa) boş olursa olsun doğru çalışacağından emin olun. Ya da farklı bir şekilde ifade etmek gerekirse : bu yol --abc-files(ve --xyz-files) yalnızca gerçek bir yol izliyorsa sağlanacaktır.
Adam Badura

1
@AdamBadura: while read ... done < <(find blah)Normal kabuk yönlendirme olduğu <yarattığı özel bir dosyadan SÜRECİ İKAMESİ . Bu, boru find blah | while read ... donehattından farklıdır, çünkü boru hattı whiledöngüyü alt kabukta çalıştırır, böylece içinde ayarlanan var (lar) sonraki komutlar için korunmaz.
dave_thompson_085

3

IFS = newline (hiçbir dosya adının newline içermediği varsayılarak) kullanabilirsiniz, ancak değişiklikten ÖNCE dış kabuğa ayarlamanız gerekir:

$ ls -1
a file with spaces
able
alpha
baker
boo hoo hoo
bravo
$ # note semicolon here; it's not enough to be in the environment passed
$ # to printf, it must be in the environment OF THE SHELL WHILE PARSING
$ IFS=$'\n'; printf '%s\n' --afiles $(find . -name 'a*') --bfiles $(find . -name 'b*')
--afiles
./able
./a file with spaces
./alpha
--bfiles
./bravo
./boo hoo hoo
./baker

İle zshama bashnull de kullanabilirsiniz $'\0'. Hatta içinde bashgibi hiçbir zaman kullanılmaz biri yeterince tuhaf karakter var mı, yeni satır kaldırabileceğinden

 IFS=$'\1'; ... $(find ... -print0 | tr '\0' '\1') ...

Ancak, bu yaklaşım, @ steeldriver'ın --afiles a boşsa atlamayı yanıtlamasıyla ilgili yorumlarda yaptığınız ek talebi işlemez.


Bash'te anladığım kadarıyla, IFSayrılmaya zorlamanın bir yolu yok nullmu?
Adam Badura

@AdamBadura: Emin değilim; bash, IFS dahil hiçbir değişkende boş bayta izin vermez. Not read -d ''steeldriver en yöntemlerinde kullanılan bir olan boş dize boş bayt içermeyen bir. (Ve bir komut seçeneği zaten böyle bir şey değil.)
dave_thompson_085

Ayrıca set -o noglob, split + glob operatörünü (hariç zsh) kullanmadan önce globbing'i ( ) devre dışı bırakmanız gerekir .
Stéphane Chazelas


@AdamBadura Evet, Bash'da, bir boş, aynı $'\0've aynıdır ''.
Isaac

1

Neden vazgeçtiğini anladığımdan emin değilim xargs.

Yani xargsbir arama yapmak hala diğeri ile başa çıkmak için bana bırakıyor…

Dize --xyz-files, birçok argümandan sadece biridir ve programınız tarafından yorumlanmadan önce bunu özel olarak değerlendirmek için bir neden yoktur. Her xargsiki findsonuç arasından da geçebileceğini düşünüyorum :

{ find -L some/dir -name \*.abc -print0 | sort -z; echo -ne "--xyz-files\0"; find -L other/dir -name \*.xyz -print0 | sort -z; } | xargs -0 ./program --abc-files

Haklısın! Bu da işe yarıyor! Ancak -print0ikinci olarak kaçırdığınıza dikkat edin find. Ayrıca bu şekilde devam eğer koyardı --abc-filesbir şekilde echosadece tutarlılık için - de.
Adam Badura

Bu yaklaşım, dizi yaklaşımından daha basit ve bir şekilde daha fazla tek katmanlı görünmektedir. Bununla birlikte, eğer .abcdosya yoksa, o zaman da --abc-files(aynı ile .xyz) olmaması gerektiği için bazı ekstra mantık gerektirir . Dizi tabanlı çözüm ile steeldriver da bunun için ekstra mantığı gerektiren ama burada değil çok önemsiz olması bu çözümün ana avantajı yok olabilir iken bu mantığı yok önemsiz olduğunu - basitlik.
Adam Badura

Ayrıca ben gerçekten emin değilim ama varsayalım xargsargümanları bölünmüş ve, bir yerine birkaç komutları yapmaya asla sürece o olduğunu açıkça öylesine yapmak için talimat -L, --max-lines( -l), --max-args( -n) veya --max-chars( -s) argümanları. Haklı mıyım? Yoksa bazı temerrütler var mı? Programım böyle bir bölünmeyi doğru şekilde işlemeyeceği ve onu aramakta başarısız olacağım ...
Adam Badura

1
@AdamBadura Eksik -print0- düzeltildi, teşekkürler. Tüm cevapları bilmiyorum ama çözümümün ekstra mantık eklemeyi zorlaştırdığına katılıyorum. Şimdi bu yaklaşımı bildiğimde muhtemelen kendim dizilerle giderdim. Cevabım senin için değildi. Diğer cevabı zaten kabul etmiştiniz ve probleminizin çözüldüğünü varsaydım. Sadece birden fazla kaynaktan argümanları aktarabileceğinizi belirtmek istedim xargs, ki bu ilk bakışta belirgin değildi. Bunu bir kavram kanıtı olarak değerlendirebilirsiniz. Şimdi hepimiz birkaç farklı yaklaşım biliyoruz ve her özel durumda bize neyin uyduğunu bilinçli olarak seçebiliriz.
Kamil Maciorowski

Evet, dizi tabanlı çözümü zaten uyguladım ve çekicilik gibi çalışıyor. Ben özellikle ne kadar temiz seçeneği ile ilgilenir gurur duyuyorum (hiçbir dosya o zaman hayır --abc-files). Ama haklısın - alternatiflerini bilmek güzel! Özellikle yanlışlıkla mümkün olmadığını düşündüm.
Adam Badura
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.