Bir dizindeki dosyalar arasında yinelemeli yineleme


15

Bir dizindeki dosyalar arasında yinelemeli olarak yineleme yapmak şu şekilde yapılabilir:

find . -type f -exec bar {} \;

Bununla birlikte, yukarıdakiler, çok sayıda koşullu dal, döngü vb. Yapılması gereken daha karmaşık şeyler için işe yaramaz. Bunu yukarıdaki için kullanıyordum:

while read line; do [...]; done < <(find . -type f)

Ancak, bu belirsiz karakterler içeren dosyalar için işe yaramaz gibi görünüyor:

$ touch $'a\nb'
$ find . -type f
./a?b

Bu tür belirsiz karakterleri iyi işleyen bir alternatif var mı?


1
find ... -exec bash -c 'echo filename is in \$0: "$0"' {} \;bunu yapmanın daha iyi bir yoludur.
jw013

Bunu çözmek ve sizin değiştirerek, orijinal tasarım tutabilir read lineTo IFS= read -r line. O zaman onu kıracak tek karakter yeni satır.
Patrick

1
@Patrick, ancak dosya adları yeni satırlar içerebilir. Bu yüzden -d $'\0'tercih edilir.
godlygeek

Yanıtlar:


7

Güvenlifind için başka bir kullanım :

while IFS= read -r -d '' -u 9
do
    [Do something with "$REPLY"]
done 9< <( find . -type f -exec printf '%s\0' {} + )

(Bu herhangi bir POSIX ile çalışır find, ancak kabuk kısmı bash gerektirir. * BSD ve GNU find ile -print0bunun yerine kullanabilirsiniz -exec printf '%s\0' {} +, biraz daha hızlı olacaktır.)

Bu, döngü içinde standart girişin kullanılmasını mümkün kılar ve herhangi bir yolla çalışır .


1
Bakmak zorunda olduğum için: "okuma ... Hiçbir ad verilmemişse, okuma satırı REPLY değişkenine atanır." Yanido echo "Filename is '$REPLY'"
Andrew

9

Bunu yapmak şu kadar basittir:

find -exec sh -c 'inline script "$0"' {} \;

Veya...

find -exec executable_script {} \;

5

En basit (ancak güvenli) yaklaşım kabuk globbing kullanmaktır:

$ for f in *; do printf ":%s:\n" "$f"; done 
:a b:
:c
d:
:-e:
:e  f:
h:

Yukarıdaki özyinelemeyi alt dizinlere (bash) yapmak için, globstarseçeneği kullanabilirsiniz ; ayrıca dotglobadı şununla başlayan dosyalarla eşleşecek şekilde ayarlanmıştır .:

$ shopt -s globstar dotglob
$ for f in **/*; do printf ":%s:\n" "$f"; done 
:a b:
:c
d:
:-e:
:e  f:
:foo:
:foo/file1:
:foo/file two:
h:

**/Biraz 4.2 bash, dizinlere sembolik bağlantılar içine recurses dikkat edin . Bash 4.3'ten bu yana, **/yalnızca dizinlere geri çekilir find.

Başka ortak bir çözüm kullanıma için find -print0olan xargs -0:

$ touch -- 'a b' $'c\nd' $'e\tf' $'g\rh' '-e'
$ find . -type f -print0 | xargs -0 -I{} printf ":%s:\n" {}
h:/g
:./e    f:
:./a b:
:./-e:
:./c
d:

h:/gDosya adı bir içerdiğinden , bunun gerçekten doğru olduğunu unutmayın \r.


4

Bu portably senin okuma döngü yapmak zor biraz, ama özellikle de partisi yüzünden gibi bir şey deneyebilirsiniz bu .

İlgili bölüm:

while IFS= read -d $'\0' -r file ; do
        printf 'File found: %s\n' "$file"
done < <(find . -iname 'foo*' -print0)

Bu find, çıktısını NUL karakterleri (0x00) ile ayrılmış olarak yazdırmayı ve readNUL ile ayrılmış satırları ( -d $'\0') ters eğik çizgileri diğer karakterler ( -r) için kaçış olarak ele almadan ve satırlar ( IFS=) üzerinde herhangi bir sözcük bölmesi yapmadan almayı söyler . 0x00, Unix'teki dosya adlarında veya yollarda oluşamayan bir bayt olduğundan, tüm garip dosya adı sorunlarınızın üstesinden gelmelidir.


1
-d ''eşittir -d $'\0'.
l0b0
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.