"find" sonucunu dosya listesi olarak nasıl aktarabilirim?


11

Durum şu mpg321ki, bir dosya listesini argüman olarak kabul eden bir MP3 çalarım var. Müziğimi "music" adlı bir dizinde saklıyorum, burada birkaç dizin daha var. Sadece hepsini oynamak istiyorum, bu yüzden programı

mpg321 $(find /music -iname "*\.mp3")

. Sorun, bazı dosya adlarının içinde boşluk olması ve programın bu adları daha küçük parçalara ayırması ve eksik dosyalar hakkında şikayet etmesidir. Sonucunu findtırnak içine alma

mpg321 "$(find /music -iname "*\.mp3")"

yardımcı olmaz çünkü hepsi büyük bir "dosya adı" haline gelir, ki bu açıkça bulunmaz.

Bunu nasıl yapabilirim? Bu önemliyse kullanıyorum bash, ama zshyakında geçeceğim .

Yanıtlar:


17

Find -print0veya -printfseçeneğini şu şekilde birlikte kullanmayı deneyin xargs:

find /music -iname "*\.mp3" -print0 | xargs -0 mpg321

Bunun nasıl çalıştığı find'in manuel sayfasında açıklanmaktadır :

-print0

Doğru; standart çıktıya tam dosya adını, ardından boş bir karakter (-print'in kullandığı yeni satır karakteri yerine) yazdırır. Bu, yeni satırlar veya diğer beyaz boşluk türlerini içeren dosya adlarının, bulma çıktısını işleyen programlar tarafından doğru şekilde yorumlanmasına olanak tanır. Bu seçenek xargs'ın -0 seçeneğine karşılık gelir.


Tüm düzenlemeler için özür dilerim. Diğer beyaz boşluk türlerinde başarısız olan -print0 xarg -0 modelini hatırlıyorum, ancak bir örnek bulamadım.
Steven D

oh evet işe yarıyor! ama hala neden merak mpg321 $(find /music -iname "*\.mp3" -print0)etmiyorum?
phunehehe

1
Bunun iki nedenden dolayı işe yaramadığına inanıyorum: (1) dosya adları boş karakterlerle ayrılmıştır, bu da mpg321'in beklediği şey değildir ve (2) xargs diğer boşluktan kaçma işini yapmıyor, değil bulabilirsiniz.
Steven D

2
@phunehehe, @Steven: mpg321onunla hiçbir ilgisi yok, çıkışını findayrı argümanlara ayıran kabuk . Ve -print0 | xargs -0olası her dosya adıyla çalışacaktır.
Gilles 'SO- kötü olmayı kes

8
find /music -iname "*\.mp3" -exec mpg123 {} +

GNU find ile de kullanabilirsiniz -print0vexargs -0 ancak başka bir araç öğrenmenin çok az anlamı vardır. -exec ... {} +Linux daha geç elde ettiklerini çünkü sözdizimi az söz alır -print0, ama şimdi kullanmak için hiçbir neden yoktur.

Zsh veya bash 4 ile bu çok daha basit:

mpg123 **/*.[Mm][Pp]3

Yalnızca zsh'de, (a) modelinin büyük / küçük harf duyarsız olmasını sağlayabilirsiniz:

mpg123 (#i)**/*.mp3

+ 1, "find -print0" çirkin bir hack.
p-statik

2

Bence Steven'ın çözümü en iyisidir, ancak başka bir yol, -Idaha sonra argümanla komutta değiştirilecek bir dizeyi belirtmenize izin veren xargs bayrağını kullanmaktır (yalnızca argümanı komutun sonuna eklemek yerine). Bunu, argümanı alıntılamak için kullanabilirsiniz:

find /music -iname "*\.mp3" | xargs -0 -Ifoo mpg321 "foo"

Bu cevabı anlamıyorum ... xargs'ın -0bayrağını bulmadan kullanıyorsunuz -print0. Ayrıca, xargs'ın -Ibayrağının bir takım sonuçları vardır. Sadece düz aksine xargstek bir komutta Stdin'den hatların hepsi sıkıştırmak, hangi xargs -Içalıştırır mpg321potansiyel kesinlikle niyet değil (her dosya için bir kez) kez yüzlerce veya binlerce. Ayrıca, foo'dan alıntı yapmanın, xargsdahili olarak yaptığı gibi gereksiz olduğunu da unutmayın . Tüm dosya adlarını satır satırına sıkıştırır ve adresine gönderirseniz xargs -I, açılmazlar.
Altı

1

Genellikle doğrudan en iyisidir, -exec ${tgt_process} \{\} +ancak herhangi bir nedenle bir dosyada veya akışta güvenilir bir şekilde sınırlandırılmış dosya adları listesi almanız gerekiyorsafind , bunu yapabilirsiniz:

find -exec sh -c 'printf "///%s///\n" "$@"' -- \{\} +

Bundan elde ettiğiniz şey iki benzersiz dizgidir. Her dosya adının başında dize \n///ve her dosya adının kuyruğunda dize bulunur ///\n. Bu iki dize find, dosya adlarının içerdiği karakterlerden bağımsız olarak, bu konumlar dışında, çıktılarında başka hiçbir yerde gerçekleşmez .

Ayrıca, yukarıdaki kullanım temel POSIX taşınabiliridir ve neredeyse tüm unix sistemlerinde çalışmaya güvenilebilir. Bu, bazılarına göre önerilen - boşluğuna rağmen - boş bir bayt sınırlayıcısının kullanımı için doğru değildir.

Ancak, yine, bu sadece herhangi bir nedenle doğrudan -execsizin yapamıyorsanız gereklidir $tgt_process, çünkü bu sizin hedefiniz olmalıdır. Bir kere, yukarıdaki yöntem hala ayrıştırma gerektirir. Örneğin, alıntılanan her dosya adı kabuğunun olmasını istiyorsanız, önce dosya adındaki sabit tırnak işaretlerinin kaçtığından emin olmanız gerekir:

find ... + | sed 's|'\''|&"&"&|g;s|///|'\''|g'

Bu, kurucu karakterleri ne olursa olsun, uygun şekilde kabuktan kaçan bir dosya adı dizisi çıkarır. Şimdi sadece alıcı uçtaki uygulamanızın onu değiştirmeyeceğini ummalısınız.


0

Bunu yapmanın bir başka yolu, dosya adlarınızda gelen tüm özel karakterlerden kaçmaktır. Örneğin:

find /music -iname "*\.mp3" | sed 's!\([] \*\$\/&[]\)!\\\1!g' | xargs mpg321

Bu, temel olarak düzgün bir şekilde kaçan dosya adlarını yürütme için xargs'a iletir ve herhangi bir sorun olmaz.


1
Bu hala dosya adlarındaki yeni satırları kırıyor. Ve sadece siz de yapabilirsiniz sed 's|.|\\&|g'- POSIX'in önerdiği şey budur.
mikeserv
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.