Alt klasörlerde belirli bir uzantıya sahip tüm dosyalarda zarif bir şekilde nasıl işlem yapabilirim?


18

Şu anki en iyi bahisim:

for i in $(find . -name *.jpg); do echo $i; done

Sorun: Dosya adlarındaki boşlukları işlemiyor.

Not: Bunu "ağaç" komutu gibi grafiksel bir şekilde yapmak isterdim.


Basitçe alıntılar yapamaz mısınız $i? Bunu yapıyorum ve iyi çalışıyor. Bu yaklaşımda bir sorun mu var?
Umar Farooq Khawaja

Yanıtlar:


26

Kanonik yol yapmak

find . -name '*.jpg' -exec echo {} \;

( bir kerede birden fazla dosya aktarmak için \;ile değiştirin )+echo

veya (GNU'ya özgü, ancak bazı BSD'lerde de var):

find . -name '*.jpg' -print0 | xargs -r0 echo

zsh:

for i (**/*.jpg(D)) echo $i

2
Eğer kullanıyorsanız xargs -0 , sen onu aynı olmalıdır find -0
itsbruce

1
@ MichaelKjörling, alıntı yapmak {} fark etmez. {} kabuk tarafından değil, find ile genişletildi. echoGibi kaçış dizilerini \b(en azından Unix uyumluları echo) genişleten bir gelişme kullanılmayacaktır .
Stéphane Chazelas

1
@sch Emin misin? findManpage gösterir find . -type f -exec file '{}' \;içinde EXAMPLESbölümü. Belki bazı mermiler diş tellerini özel olarak tedavi eder.
QuasarDonkey

1
Tırnak işaretleri zarar vermez, ancak fark yaratmaz. Tüm '{}', \{\}, {}, "{}", '{'}"{" ve "}" iki karakterdir bulmak için bir argüman için kabuk (her ne Bourne benzeri kabuk) tarafından genişletilir. "Find" ifadesini "echo find" ile değiştirin veya printf '<%s>\n' findtekrar kontrol etmek istiyorsanız.
Stéphane Chazelas


2

Daha iyi yanıtlar verilmiş.

Ancak, kodda verilen boşlukların boşluk olmadığını unutmayın. sekme, satırsonu ve joker karakterler de bir sorundur.

İle

IFS='
'
set -f
for i in $(find . -name '*.jpg'); do echo "$i"; done

O zaman sadece yeni satır karakterleri sorun olur.


1

Bahsettiğiniz, dosya adlarını okumaya çalışırken insanların karşılaştığı temel sorunlardan biridir. Bazen, sınırlı bilgiye sahip olan ve dosya ve klasör yapısını yanlış anlayan insanlar " UNIX'te Herşey bir dosyadır " ını unutmaya eğilimlidir . Bu nedenle, dosya adı boşluklu 2 veya daha fazla kelimeden oluşabileceğinden boşlukları da işlemeleri gerektiğini anlamıyorlar.

Çözüm: Bunu yapmanın daha iyi bilinen yollarından biri temiz bir okuma yapmaktır .

Burada mevcut tüm dosyaları okuyacağız ve bir değişken içinde tutacağız, bir dahaki sefere istenen işlemi yaparken dosya değişkenlerini boşluklarla koruyacak tırnak işaretleri içinde tutmamız gerekecek. Bu, bunu yapmanın temel yollarından biridir, ancak burada diğer insanlar tarafından sağlanan diğer cevaplar da aynı şekilde çalışır.

SENARYO :

 find /path/to/files/ -iname "*jpg" | \
  while read I; do
   cp -v --parent "$I" /backup/dir/
  done

Burada onlara yol vererek dosyaları okuyorum ve ne okuyorsam ben onları daha sonra doğru şekilde işlemek için boşlukları korumak için işlerken daha sonraki bir noktada teklif edecek bir değişken I içinde saklıyorum.

Umarım bu size bir şekilde yardımcı olur.


1
"her şey bir dosyadır" başka bir şeyi ifade eder. Çözümünüz GUN'a özgüdür ve dosya adlarında ters eğik çizgi veya yeni satır karakterleriyle başa çıkmaz. Kabuklardaki "IMO" döngüleri (IMO) genellikle kötü kabuk komut dosyası uygulamalarının bir göstergesidir.
Stéphane Chazelas

@sch: ha, ve eskiden düşünürdüm, sorun değil. Bunu işaret ettiğiniz için teşekkür ederim, muhtemelen daha fazla bakmam gerekiyor.
Kara Şövalye

üzgünüm, "GNU" demek istedim, yukarıdaki "GUN" değil (bunların BTW anagramları olduğunu ilk kez anlıyorum ...)
Stéphane Chazelas

1

Sadece findve ile bash:

find . -name '*.jpg' -exec bash -c '
    echo "treating file $1 in bash, from path : ${1%/*}" 
' -- {} \;

Bu şekilde, $1bash'de, temel bir komut dosyasında olduğu gibi, gelişmiş (veya değil) görevleri yerine getirmek için güzel perspektifler açıyoruz.

Daha etkili bir yol (her dosya için yeni bir bash başlatmak zorunda kalmamak için):

find . -name '*.jpg' -exec bash -c 'for i do
    echo "treating file $i in bash, from path : ${i%/*}" 
  done' -- {} +

0

Bash'ın son sürümünde, globstarseçeneği kullanabilirsiniz :

shopt -s globstar
for file in **/*.jpg ; do
    echo "$file"
done

Basit işlemler için, döngüyü tamamen atlayabilirsiniz:

echo **/*.jpg

Bu, zsh'de (özelliğin kaynaklandığı yerde) ve ksh93 (ile set -G) ile çalışırken, bash sürümü, grep -rdizinlere ( find -Lveya zsh'lara eşdeğer) bağlı olduğu için bash sürümü temelde kırıldığı için (tıpkı GNU gibi ) kullanılmamalıdır. ***/*.jpg). Ayrıca, bulmanın aksine, nokta dosyalarını ommitleyeceğini ve dotdirs'e (varsayılan olarak) inmeyeceğini unutmayın.
Stéphane Chazelas
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.