YAPMAYIN dizinler bulmak bir dosya


58

Evet, müziğimi sıralıyorum. Aşağıdaki mantrada her şeyi güzelce düzenledim: /Artist/Album/Track - Artist - Title.extve eğer varsa, kapak oturur /Artist/Album/cover.(jpg|png).

Tüm ikinci seviye dizinleri taramak ve kapağı olmayanları bulmak istiyorum. İkinci aşamada, /Britney Spears/bir cover.jpg olmasın umrumda değil, ama umurumda /Britney Spears/In The Zone/değil.

Kapak indirme konusunda endişelenmeyin (bu yarın benim için eğlenceli bir proje) Sadece ters-ish findörneği hakkında görkemli bash-fuiness ile ilgileniyorum .


Eksik kapakları indirmekle ilgilenen herkes için, launchpad.net/coverlovin dosyasını yükleyin ve @phoibos yanıtındaki -print'i "-exec ./coverlovin.py {} \;" ile değiştirin
Dror Cohen

Yanıtlar:


81

Durum 1: Tam olarak aranacak dosya adını biliyorsunuz

Bir dosya olup olmadığını kontrol etmek için findile kullanın test -e your_file. Örneğin, içinde bulunmayan dizinleri cover.jpgararsınız:

find base_dir -mindepth 2 -maxdepth 2 -type d '!' -exec test -e "{}/cover.jpg" ';' -print

Yine de büyük / küçük harf duyarlı.

2. Durum: Daha esnek olmak istiyorsunuz

Dava emin değiliz ve uzatma olabilir jPg, png...

find base_dir -mindepth 2 -maxdepth 2 -type d '!' -exec sh -c 'ls -1 "{}"|egrep -i -q "^cover\.(jpg|png)$"' ';' -print

Açıklama:

  • shKullanırken borulamanın yapılamaması nedeniyle her bir dizin için bir kabuk oluşturmanız gerekir.find
  • ls -1 "{}"çıkışları sadece dizinin dosya findanda kateder
  • egrep(yerine grep) genişletilmiş düzenli ifadeler kullanır; -iarama durumunu duyarsız -qkılar , çıktıları çıkarır
  • "^cover\.(jpg|png)$"Arama kalıbıdır. Bu örnekte, örneğin maçları cOver.png, Cover.JPGveya cover.png. .O maçları demektir aksi şekilde çıkmalıdır herhangi bir karakter. ^çizginin başlangıcını $, sonunu işaretler

Egrep için diğer arama şablonu örnekleri :

Parçayı egrep -i -q "^cover\.(jpg|png)$"şununla değiştir:

  • egrep -i -q "cover\.(jpg|png)$": Ayrıca maçlar cd_cover.png, album_cover.JPG...
  • egrep -q "^cover\.(jpg|png)$": Eşleşir cover.png, cover.jpgfakat DEĞİL Cover.jpg(büyük küçük harf duyarlılığı kapalı değil)
  • egrep -iq "^(cover|front)\.jpg$": Örneğin maçları front.jpg, Cover.JPGama değil Cover.PNG

Bu konuda daha fazla bilgi için Normal İfadelere bakın .


Kesinlikle çok güzel - kasalar veya farklı uzantılar arasında seçim yapmak esnek olmadığından (joker karakter kullanmamıştım ama kullanmamıştım). Acaba daha iyi bir alternatif var mı test?
Oli

1
Hmm'yi bulup iç içe geçirebilirsiniz -exec bash -c '[[ -n $(find "{}" -iname "cover.*") ]]' \;ancak bu optimizasyon açısından oldukça pis. Yine de işe yarıyor.
Oli

OR sorguları için testbir yük iletebileceğini öğrendim -o EXPRESSION... örneğin: test -e "{}/cover.jpg" -o -e "{}/cover.png"tam gelişmiş bir arama yapmaktan daha iyidir, ancak yine de büyük / küçük harfe duyarlıdır.
Oli

Bunun gereğidir (bulmak comm'd ve globbing comm'd) diğer iki çözümden karşı (benim son yorum başına, iki test) bu performanslarını karşılaştırarak dikkat etmelidir yavaş (684ms vs 40ms ve 50ms sırasıyla)
Oli

Orijinal cevaplama çözümü bir saniyeden fazla sürüyor $ve dir. Adı altında olan durumlarda kırılıyor (örneğin, Ke $ ha).
Oli

12

Basit, o geçer. Aşağıdaki, kapaklı bir dizin listesi alır ve bunu tüm ikinci seviye dizinlerin listesiyle karşılaştırır. Her iki "dosyada" görünen satırlar gizlenerek, kapak gerektiren dizinlerin bir listesi bırakılır.

comm -3 \
    <(find ~/Music/ -iname 'cover.*' -printf '%h\n' | sort -u) \
    <(find ~/Music/ -maxdepth 2 -mindepth 2 -type d | sort) \
| sed 's/^.*Music\///'

Yaşasın.

Notlar:

  • comm'nin argümanları aşağıdaki gibidir:

    • -1 file1'e özgü satırları bastır
    • -2 file2'ye özgü satırları bastır
    • -3 her iki dosyada da görünen satırları bastır
  • commsadece dosyaları alır, bu yüzden sıkıcı <(...)giriş yöntemi. Bu, içeriği gerçek bir [geçici] dosya aracılığıyla aktarır.

  • commsıralı giriş gerekiyor veya çalışmıyor ve findhiçbir şekilde bir sipariş garantisi yok. Aynı zamanda benzersiz olması gerekiyor. İlk findişlem için birden fazla dosya bulabilir, cover.*böylece tekrarlanan girdiler olabilir. sort -uhızlı bir şekilde bunları karıştırır. İkinci buluş her zaman benzersiz olacak.

  • dirnamesed(ve ark) başvurmadan bir dosyanın dir almak için kullanışlı bir araçtır .

  • findve commher ikisi de çıktıları ile biraz karışık. Nihai sedsen kalacaksın böylece şeyleri temizlemek için vardır Artist/Album. Bu sizin için arzu edilebilir veya olmayabilir.


2
İlk findkutu muhtemelen için Basitleştirilmiş find ~/Music/ -iname 'cover.*' -printf '%h\n'ihtiyacını gidermiş dirname. dirnamebaşka bir yerde kullanışlı olsa .
Tom

Thanks @Tom, bu her yerde unutmaktan çok daha hızlı (müzik direktörümde 29ms vs 734ms - her ikisi de "sıcak" buluyor)
Oli

9

Bu, globbing ile çözmek için bulmaktan çok daha hoş.

$ cd ... # to the directory one level above the album/artist structure

$ echo */*/*.cover   # lists all the covers

$ printf "%s\n" */*/*.cover # lists all the covers, one per line

Şimdi bu güzel yapıda başıboş dosyalarınız olmadığını varsayalım. Geçerli dizin yalnızca sanatçı alt dizinlerini ve bunlar yalnızca albüm alt dizinlerini içerir. O zaman şöyle bir şey yapabiliriz:

$ diff  <(for x in */*/cover.jpg; do echo "$(dirname "$x")" ; done) <(printf "%s\n" */*)

<(...)Sözdizimi Bash süreç ikamesi şudur: Eğer bir dosya argümanı yerine bir komut kullanmanızı sağlar. Bir komutun çıktısını bir dosya olarak görmenizi sağlar. Böylece iki programı çalıştırabilir ve çıktılarını geçici dosyalara kaydetmeden farklarını alabiliriz. diffProgram iki dosya ile çalışıyor, ama aslında iki boru okuma sanıyor.

Sağdaki girişini üreten komut diff, printf "%s\n" */*sadece albüm dizinleri listeler. Sol el komutu *.coveryollarda yinelenir ve dizin adlarını yazdırır.

Test sürüşü:

$ find .   # let's see what we have here
.
./a
./a/b
./foo
./foo/bar
./foo/baz
./foo/baz/cover.jpg

$ diff  <(for x in */*/cover.jpg; do echo "$(dirname "$x")" ; done) <(printf "%s\n" */*)
0a1,2
> a/b
> foo/bar

Aha, a/bve foo/bardizinleri yok cover.jpg.

Bazı kırık köşe kasaları vardır, varsayılan olarak *eğer bir şeyle eşleşmezse kendiliğinden genişler. Bu Bash'in ile ele alınabilir set -o nullglob.


Geç cevap için özür dilerim. Bu ilginç bir fikir ama: png ve jpb'de kapaklar olabilir ve bundan commdaha temiz olmaz mıydı diff?
Oli

comm -3 <(printf "%s\n" */*/cover* | sed -r 's/\/[^\/]+$//' | sort -u) <(printf "%s\n" */*)hiçbiri diffkabartmasız makul bir uzlaşma gibi görünüyor . Bununla birlikte, çift bulucumdan biraz daha yavaş.
Oli

0
ls --color=never */*.txt | sed 's|/.*||' | sort -u -n > withtxt.txt
ls --color=never -d * | sort -u -n > all.txt
diff all.txt withtxt.txt

İçinde txt dosyası olmayan tüm dizinleri gösterecektir.

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.