Adı bir dize içeren bir dosya içeren alt dizinlerin listesini alın


45

Adı belirli bir düzenle eşleşen bir dosyayı içeren alt dizinlerin listesini nasıl alabilirim?

Daha spesifik olarak, dosya adında bir yerde 'f' harfini içeren bir dosya içeren dizinler arıyorum.

İdeal olarak, listede çoğaltmalar olmaz ve yalnızca dosya adı olmayan yolu içerir.

Yanıtlar:


43
find . -type f -name '*f*' | sed -r 's|/[^/]+$||' |sort |uniq

Yukarıdakiler, geçerli dizinin altındaki ( .) normal dosyalar ( -type f) olan ve fadlarında bir yerde bulunan tüm dosyaları bulur ( -name '*f*'). Daha sedsonra, sadece dizin adını bırakarak dosya adını kaldırır. Sonra, dizinlerin listesi sıralanır ( sort) ve kopyalar kaldırılır ( uniq).

sedKomutu tek yerine oluşur. Düzenli ifadeyle eşleşmeleri arar /[^/]+$ve eşleşen hiçbir şeyi hiçbir şeyle değiştirmez. Dolar işareti çizginin sonu anlamına gelir. [^/]+'eğik çizgi olmayan bir veya daha fazla karakter anlamına gelir. Böylece, /[^/]+$son eğik çizgiden çizginin sonuna kadar olan tüm karakterler anlamına gelir. Başka bir deyişle, bu tam yolun sonundaki dosya adıyla eşleşir. Bu nedenle, sed komutu dosya adını kaldırır ve dosyanın içinde bulunduğu dizinin adını değiştirmeden bırakır.

basitleştirmeler

Modern sortkomutların çoğu gereksiz -uyapan bir bayrağı destekler uniq. GNU sed için:

find . -type f -name '*f*' | sed -r 's|/[^/]+$||' |sort -u

Ve MacOS sed için:

find . -type f -name '*f*' | sed -E 's|/[^/]+$||' |sort -u

Ayrıca, findkomutunuz destekliyorsa, finddoğrudan dizin adlarını yazdırmanız da mümkündür . Bu, şu gereksinimden kaçınır sed:

find . -type f -name '*f*' -printf '%h\n' | sort -u

Daha sağlam sürüm (GNU araçları gerektirir)

Yukarıdaki sürümlerde yeni satırlar içeren dosya adları ile karıştırılacak. NUL sonlandırılmış dizgilerde sıralama yapmak daha sağlam bir çözümdür:

find . -type f -name '*f*' -printf '%h\0' | sort -zu | sed -z 's/$/\n/'

Hepsini sıralamanızı sağlayan çok fazla dosyam var. uniqKarışımı atmak , zaten yan yana olan tekrarlanan çizgileri kaldırarak çok yardımcı olur. find . -type f -name '*f*' -printf '%h\0' | uniq -z | sort -zu | tr '\0' '\n'. Veya aletleriniz biraz daha eskiyse, uniq -z seçeneğine sahip olmayabilir. find . -type f -name '*f*' -printf '%h\n' | uniq | sort -u
jbo5112

1
MacOS Kullanıcıları: sed bayrağı -r değildir. Nedense onun -E
David

@David Çok doğru. -EMacOS'ta gösterilmek üzere cevap güncellendi .
John1024

22

Neden bunu denemiyorsun:

find / -name '*f*' -printf "%h\n" | sort -u

En iyi cevap. Tamamen POSIX uyumlu, yukarıdaki bazı cevapların aksine, ve aynı zamanda özel En Kısa Boru Hattı ödülü :).
kkm

Birinin yukarıdaki zamana göre diğerlerinin zamanlamasını göstermesini çok isterim, çünkü bunun en hızlı olduğu hissine kapılıyorum.
dlamblin

4
@kkm ben bu en iyi çözüm ama katılıyorum için POSIX gözlükfind aslında oldukça seyrek-olan -printfoperatör olduğunu değil belirtilmedi. Bu BSD ile çalışmıyor find. Yani, "tamamen POSIX uyumlu" değil . (Her sort -u ne kadar POSIX'te .)
Wildcard

8

Bunu yapmak için kullanabileceğiniz 2 yöntem var. Biri dize ayrıştırırken diğeri her dosya üzerinde çalışacaktır. İpin ayrıştırılması gibi bir araç kullanın , ya da açıkça daha hızlı olacağına dair bir araç kullanın grep, fakat işte size her ikisini de gösteren ve 2 yöntemi nasıl "profilleyebileceğinizi" gösteren bir örnek.sedawk

Örnek veri

Aşağıdaki örnekler için aşağıdaki verileri kullanacağız

$ touch dir{1..3}/dir{100..112}/file{1..5}
$ touch dir{1..3}/dir{100..112}/nile{1..5}
$ touch dir{1..3}/dir{100..112}/knife{1..5}

*f*Dosyalardan bazılarını sil dir1/*:

$ rm dir1/dir10{0..2}/*f*

Yaklaşım # 1 - Dizeler yoluyla ayrıştırma

Burada, aşağıdaki araçları kullanacağız find, grepve sort.

$ find . -type f -name '*f*' | grep -o "\(.*\)/" | sort -u | head -5
./dir1/dir103/
./dir1/dir104/
./dir1/dir105/
./dir1/dir106/
./dir1/dir107/

Yaklaşım # 2 - Dosyaları kullanarak ayrıştırma

Daha önce olduğu gibi aynı takım zinciri, dirnamebunun yerine kullanacağımız bu süre dışında grep.

$ find . -type f -name '*f*' -exec dirname {} \; | sort -u | head -5
./dir1/dir103
./dir1/dir104
./dir1/dir105
./dir1/dir106
./dir1/dir107

NOT: Yukarıdaki örnekler head -5sadece bu örnekler için uğraştığımız çıktı miktarını sınırlamak için kullanılmaktadır . Tam listeni almak için normalde kaldırılırlar!

Sonuçların karşılaştırılması

time2 yaklaşıma bakmak için kullanabiliriz .

dizinadı

real        0m0.372s
user        0m0.028s
sys         0m0.106s

grep

real        0m0.012s
user        0m0.009s
sys         0m0.007s

Yani mümkünse dizelerle başa çıkmak her zaman en iyisidir.

Alternatif dize ayrıştırma yöntemleri

grep ve PCRE

$ find . -type f -name '*f*' | grep  -oP '^.*(?=/)' | sort -u

sed

$ find . -type f -name '*f*' | sed 's#/[^/]*$##' | sort -u

awk

$ find . -type f -name '*f*' | awk -F'/[^/]*$' '{print $1}' | sort -u

+1 Çünkü işe yarıyor, ama ilginç bir şekilde @ John1024'ün cevabından çok daha uzun sürüyor
Muhd

@Muhd - evet, dirname çağrıları yavaş. Bir alternatif üzerinde çalışıyorum.
slm

2

İşte yararlı buluyorum:

find . -type f -name "*somefile*" | xargs dirname | sort | uniq

1

Bu cevap utanmadan slm cevabına dayanmaktadır. İlginç bir yaklaşımdı, ancak dosya ve / veya dizin isimlerinin özel karakterleri (boşluk, yarı sütun ...) olsaydı bir kısıtlaması vardı. İyi bir alışkanlık kullanmaktır find /somewhere -print0 | xargs -0 someprogam.

Örnek veri

Aşağıdaki örnekler için aşağıdaki verileri kullanacağız

mkdir -p dir{1..3}/dir\ {100..112}
touch dir{1..3}/dir\ {100..112}/nile{1..5}
touch dir{1..3}/dir\ {100..112}/file{1..5}
touch dir{1..3}/dir\ {100..112}/kni\ fe{1..5}

*f*Dosyalardan bazılarını sil dir1/*/:

rm dir1/dir\ 10{0..2}/*f*

Yaklaşım # 1 - Dosyaları kullanarak ayrıştırma

$ find -type f -name '*f*' -print0 | sed -e 's#/[^/]*\x00#\x00#g' | sort -zu | xargs -0 -n1 echo | head -n5
./dir1/dir 103
./dir1/dir 104
./dir1/dir 105
./dir1/dir 106
./dir1/dir 107

NOT : Yukarıdaki örnekler head -5sadece bu örnekler için uğraştığımız çıktı miktarını sınırlamak için kullanılmaktadır . Tam listeni almak için normalde kaldırılırlar! ayrıca echokullanmak istediğiniz komutu değiştirin .


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.