Bash kullanarak her alt dizinde bir eylem gerçekleştirin


196

Belirli bir klasörün her alt dizininde bir eylem gerçekleştirmek için gereken bir komut dosyası üzerinde çalışıyorum.

Bunu yazmanın en etkili yolu nedir?


1
Lütfen yanıtları tekrar gözden geçirmeyi ve doğruluk için cevapları yeniden değerlendirmeyi düşünün - büyük hatalara rağmen çok sayıda görüş almayı kabul eden bir cevabınız var (f / e, daha önce birisinin mkdir 'foo * bar'neden olacağı bir dizinin üzerinden çalıştırılması foove bartekrarlanmasına rağmen) bunlar mevcut değildir ve *bunların yerine dizin olmayanlar da dahil olmak üzere tüm dosya adlarının bir listesi gelir ).
Charles Duffy

1
... daha da kötüsü, eğer birisi kaçarsa mkdir -p '/tmp/ /etc/passwd /'- birisi bu uygulamayı takip /tmpetmek için, örneğin silinecek dizinleri bulmak için bir komut dosyası çalıştırırsa , silebilirler /etc/passwd.
Charles Duffy

Yanıtlar:


179
for D in `find . -type d`
do
    //Do whatever you need with D
done

81
Bu beyaz alanlarda
kırılacak

2
Ayrıca, findözyinelemeli veya özyinelemesiz davranış istiyorsanız parametreleri değiştirmeniz gerektiğini unutmayın .
Chris Tonkinson

32
Yukarıdaki cevap bana kendi kendine dizin de verdi, bu yüzden aşağıdaki benim için biraz daha iyi çalıştı:find . -mindepth 1 -type d
jzheaux

1
@JoshC, her iki varyant tarafından oluşturulan dizini mkdir 'directory name with spaces'dört ayrı kelimeye böler.
Charles Duffy

4
Eklemem gerekiyordu -mindepth 1 -maxdepth 1ya da çok derinlere iniyordu.
ScrappyDev

284

Alt işlem oluşturmayı önleyen bir sürüm:

for D in *; do
    if [ -d "${D}" ]; then
        echo "${D}"   # your processing here
    fi
done

Veya eyleminiz tek bir komutsa, bu daha özlüdür:

for D in *; do [ -d "${D}" ] && my_command; done

Ya da daha özlü bir versiyon ( teşekkürler @enzotib ). Bu sürümde, her değerinin Dsonunda eğik çizgi olacağını unutmayın:

for D in */; do my_command; done

49
Sen önleyebilirsiniz ifya [: ilefor D in */; do
enzotib

2
+1, çünkü dizin adları kabul edilen cevaba karşılık ./ ile başlamıyor
Hayri Uğur Koltuk

3
Bu, +1 dizin adlarındaki boşluklara kadar doğrudur
Alex Reinking

5
Son komutla ilgili bir sorun vardır: alt dizinleri olmayan bir dizindeyseniz; "* /" döndürecektir. Bu yüzden ikinci komutu for D in *; do [ -d "${D}" ] && my_command; doneveya en son iki kombinasyonu kullanın :for D in */; do [ -d $D ] && my_command; done
Chris Maes

5
Bu yanıtın gizli dizinleri yok saydığını unutmayın. Gizli dizinleri dahil etmek için for D in .* *; dokullanın for D in *; do.
patryk.beza

107

En basit özyinelemesiz yol:

for d in */; do
    echo "$d"
done

/Sonunda tek kullanım dizinleri, söyler.

Gerek yok

  • bulmak
  • awk
  • ...

11
Not: Bu nokta dirs'lerini içermeyecektir (bu iyi bir şey olabilir, ancak bilmek önemlidir).
wisbucky

shopt -s dotglobJoker karakterleri genişletirken dotfiles / dotdirs eklemek için kullanabileceğinizi unutmayın . Ayrıca bkz: gnu.org/software/bash/manual/html_node/The-Shopt-Builtin.html
Steen Schütt

Ne demek düşünüyorum /*yerine */birlikte /kullanmak istediğiniz yolu temsil eder.
Brōtsyorfuzthrāx

2
@Shule /*mutlak yol için kullanılırken */, şu anki konumdan alt dizinleri içerecektir
Dan G

3
faydalı ipucu: sondaki '/' karakterini $ d'den ${d%/*}
kesmeniz gerekiyorsa

18

findKomutu kullan .

GNU'da findşu -execdirparametreyi kullanabilirsiniz :

find . -type d -execdir realpath "{}" ';'

veya şu -execparametreyi kullanarak :

find . -type d -exec sh -c 'cd -P "$0" && pwd -P' {} \;

veya şu xargskomutla:

find . -type d -print0 | xargs -0 -L1 sh -c 'cd "$0" && pwd && echo Do stuff'

Veya döngü için kullanarak :

for d in */; { echo "$d"; }

Özyineleme için **/bunun yerine genişletilmiş globbing ( ) işlevini deneyin (tarafından etkinleştirin :) shopt -s extglob.


Daha fazla örnek için, bkz: Her bir dizine nasıl gidip komut nasıl çalıştırılır? SO'da


1
-exec {} +POSIX tarafından belirtilir, -exec sh -c 'owd=$PWD; for arg; do cd -- "$arg" && pwd -P; cd -- "$owd"; done' _ {} +başka bir yasal seçenektir ve daha az mermi gerektirir -exec sh -c '...' {} \;.
Charles Duffy

13

Kullanışlı tek gömlekler

for D in *; do echo "$D"; done
for D in *; do find "$D" -type d; done ### Option A

find * -type d ### Option B

A seçeneği, aralarında boşluk olan klasörler için doğrudur. Ayrıca, klasör adındaki her sözcüğü ayrı bir varlık olarak yazdırmadığından genellikle daha hızlıdır.

# Option A
$ time for D in ./big_dir/*; do find "$D" -type d > /dev/null; done
real    0m0.327s
user    0m0.084s
sys     0m0.236s

# Option B
$ time for D in `find ./big_dir/* -type d`; do echo "$D" > /dev/null; done
real    0m0.787s
user    0m0.484s
sys     0m0.308s

10

find . -type d -print0 | xargs -0 -n 1 my_command


bu bulgunun dönüş değerini bir for döngüsüne aktarabilir miyim? Bu daha büyük bir betiğin bir parçası ...
mikewilliamson

@Mike, olası değil. , $? muhtemelen find veya xargs komutunun durumunu alır my_command.
Paul Tomblin

7

Bu bir alt kabuk oluşturur (yani whiledöngüden çıkarken değişken değerlerin kaybolacağı anlamına gelir ):

find . -type d | while read -r dir
do
    something
done

Bu olmayacak:

while read -r dir
do
    something
done < <(find . -type d)

Dizin adlarında boşluk varsa her ikisi de çalışır.


Garip dosya adlarının daha iyi işlenmesi için (boşlukla biten ve / veya satır beslemeleri içeren adlar dahil), find ... -print0vewhile IFS="" read -r -d $'\000' dir
Gordon Davisson

@GordonDavisson, ... gerçekten, -d ''bash sözdizimi ve yetenekleri hakkında daha az yanıltıcı olduğunu bile iddia edebilirim , çünkü -d $'\000'(yanlış) $'\000'bir şekilde farklı olan (yanlış) '', birinden kolayca (ve tekrar, yanlış) çıkarım yapabilir bash, C dizeleri (NUL sınırlandırılmış, NUL içeremez) yerine Pascal tarzı dizeleri (uzunluk belirtilmiş, NUL değişmezlerini içerebilen) destekler.
Charles Duffy

5

Deneyebilirsiniz:

#!/bin/bash
### $1 == the first args to this script
### usage: script.sh /path/to/dir/

for f in `find . -maxdepth 1 -mindepth 1 -type d`; do
  cd "$f"
  <your job here>
done

veya benzeri...

Açıklama:

find . -maxdepth 1 -mindepth 1 -type d: Yalnızca 1 bir 1 maksimum özyinelemeli derinliği (1 $ sadece alt dizinleri) ve minimum derinliği (dışlar geçerli klasör ile dizinleri bulmak .)


Bu adamcağız - boşlukları olan bir dizin adı ile deneyin. Bkz. BashPitfalls # 1 ve DontReadLinesWithFor .
Charles Duffy

Boşluklu dizin adı tırnak işaretleri içine alınır ve bu nedenle çalışır ve OP dosyadan satır okumaya çalışmaz.
Henry Dobson

içinde çalışır cd "$f". Çıktı finddizeden ayrıldığında çalışmaz , bu nedenle adın ayrı parçalarını ayrı değerler olarak elde $federsiniz, bu da ne kadar iyi yaptığınızı veya teklif vermediğinizi $fbelirtir.
Charles Duffy

Onların demedim edildi dosyadan satırları okumaya çalışıyorum. findçıktısı, varsayılan -printeylemle birlikte satır yönelimli (bir satırdan bir ad, teoride - ancak aşağıya bakınız) .
Charles Duffy

Hat yönelimli çıktı, olduğu gibi, rasgele dosya adlarını iletmenin güvenli bir yolu find -printdeğildir , çünkü biri bir şey çalıştırabilir ve adında ayrı bir satır olan bir dizin alabilir . Dosyalardaki veya dizinlerdeki adları dikkatsizce ele almak, ayrıcalık yükseltme saldırıları almanın harika bir yoludur. mkdir -p $'foo\n/etc/passwd\nbar'/etc/passwd/upload/tmp
Charles Duffy

4

dizin adlarında varsa kabul edilen yanıt beyaz boşluklarda $()kesilir ve tercih edilen sözdizimi bash / ksh içindir. Kullanım GNU find -exec ile opsiyon +;örn

find .... -exec mycommand +; #this is same as passing to xargs

veya while döngüsü kullanın

find .... |  while read -r D
do
  ...
done 

dizin adını hangi param tutacak? örneğin, chmod + x $ DIR_NAME (evet, yalnızca dizinler için bir chmod seçeneği olduğunu biliyorum)
Mike Graf

Arasında find -execve geçişinde küçük bir fark vardır xargs: findyürütülmekte olan komutun çıkış değerini yoksayar, xargssıfır olmayan bir çıkışta başarısız olur. İhtiyaçlarınıza bağlı olarak her ikisi de doğru olabilir.
Jason Wodicka

find ... -print0 | while IFS= read -r ddaha güvenlidir - boşlukla başlayan veya biten adları ve yeni satır değişmezleri içeren adları destekler.
Charles Duffy
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.