Sadece bash içindeki dizinleri nasıl değiştiririm?


249

Bazı dizinleri ve bazı dosyaları içeren bir klasörüm var (bazıları noktadan başlayarak gizlenir).

for d in *; do
 echo $d
done

Tüm dosyalar arasında döngü, ancak yalnızca dizinler arasında döngü istiyorum. Bunu nasıl yaparım?

Yanıtlar:


332

Sonunda yalnızca dizinlerle eşleşecek bir eğik çizgi belirleyebilirsiniz:

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

7
Ayrıca dizinlere sembolik bağlantılar içerdiğini unutmayın.
Stéphane Chazelas

1
Peki o zaman sembolik bağları nasıl dışlarsın?
rubo77,

3
@ rubo77: [[ -L $d ]]$ d'nin sembolik bir bağlantı olup olmadığını test edebilirsiniz .
choroba

1
@AsymLabs Bu yanlış, set -Pyalnızca dizini değiştiren komutları etkiler. sprunge.us/TNac
Chris Down

4
@choroba: [[ -L "$f" ]]ile bu durumda sembolik bağları dışlamayacak */, takip eden eğik çizgiyi kesmek zorunda kalacaksınız [[ -L "${f%/}" ]]( takip eden eğik çizgiyle bağlantı testi için bakın )
rubo77


30

Choroba'nın çözümünün, şık olmasına rağmen, mevcut dizinde hiçbir dizin bulunmuyorsa beklenmeyen davranışlar ortaya çıkarabileceğini unutmayın. Bu durumda, atlama yerine fordöngü, bash tam olarak nerede bir kez döngü çalışır deşittir */:

#!/usr/bin/env bash

for d in */; do
    # Will print */ if no directories are available
    echo $d
done

Bu davaya karşı korunmak için aşağıdakileri kullanmanızı öneririm:

#!/usr/bin/env bash

for f in *; do
    if [ -d ${f} ]; then
        # Will not run if no directories are available
        echo $f
    fi
done

Bu kod geçerli dizindeki tüm dosyalar arasında dolaşır f, bir dizin olup olmadığını kontrol eder , sonra fkoşul doğru dönerse eko olur. Eğer feşittir */, echo $fyürütülmez.


3
Çok daha kolay shopt -s nullglob.
choroba

21

Yalnızca dizinlerin kullandığından daha belirli dosyalar seçmeniz gerekiyorsa findve bunu iletin while read:

shopt -s dotglob
find * -prune -type d | while IFS= read -r d; do 
    echo "$d"
done

shopt -u dotglobGizli dizinleri hariç tutmak için kullanın (veya setopt dotglob/ unsetopt dotglobin zsh).

IFS=$IFSörneğin birini içeren dosya adlarını bölmekten kaçınmak için:'a b'

bkz AsymLabs cevap fazlası için aşağıdaki findseçeneklerden


edit:
while döngüsünden bir çıkış değeri oluşturmanız gerekirse, ekstra alt kabuğu bu numaraya çevirebilirsiniz:

while IFS= read -r d; do 
    if [ "$d" == "something" ]; then exit 1; fi
done < <(find * -prune -type d)

2
Bu çözümü buldum: stackoverflow.com/a/8489394/1069083
rubo77

11

Bunun için saf bash kullanabilirsiniz, ancak bulmak kullanmak daha iyidir:

find . -maxdepth 1 -type d -exec echo {} \;

(ayrıca gizli dizinleri içerecek şekilde bulun)


8
Dizinlere sembolik bağlantılar içermediğine dikkat edin. Gizli dizinleri dahil etmek shopt -s dotglobiçin kullanabilirsiniz bash. Seninkiler de içerecek .. Ayrıca -maxdepthstandart bir seçenek olmadığını unutmayın ( -pruneis).
Stéphane Chazelas

2
dotglobSeçenek ilginç ama dotglobsadece kullanımı için geçerlidir *. find .her zaman gizli dizinleri (ve mevcut dizini de
içerecektir

6

Bu, kök dizini hariç, mevcut çalışma dizini içerisinde hem görünür hem de gizli dizinleri bulmak için yapılır:

sadece dizinler arasında dolaşmak için:

 find -path './*' -prune -type d

sonuçta sembolik bağlantılar eklemek için:

find -L -path './*' -prune -type d

Her dizine bir şey yapmak için (sembolik bağlantılar hariç):

find -path './*' -prune -type d -print0 | xargs -0 <cmds>

Gizli dizinleri hariç tutmak için:

find -path './[^.]*' -prune -type d

döndürülen değerler üzerinde çok sayıda komut çalıştırmak için (çok tartışmalı bir örnek):

find -path './[^.]*' -prune -type d -print0 | xargs -0 -I '{}' sh -c \
"printf 'first: %-40s' '{}'; printf 'second: %s\n' '{}'"

'sh -c' yerine 'bash -c' vb.


Echo * / 'da d için' * / 'in' ekosu, 60.000 dizin içeriyorsa ne olur?
AsymLabs

"Çok fazla dosya" hatası alırsınız, ancak bu çözülebilir:
debian'da

1
Xargs örneği yalnızca 9e.g dizin adlarının bir alt kümesi için işe yarar. boşlukları veya yeni satırları olanlar işe yaramazsa). ... -print0 | xargs -0 ...Tam adların ne olduğunu bilmiyorsanız , kullanmanız daha iyi olur .
Anthon

1
@ rubo77, gizli dosyaları / dizinleri dışlamak ve çoklu komutları çalıştırmak için bir yol ekledi - tabii ki bu da bir betikle yapılabilir.
AsymLabs

1
Cevabımın alt kısmına bir örnek ekledim, her dizine bir fonksiyon kullanarakfind * | while read file; do ...
rubo77

3

Tek bir satırda gizli dizinler (nokta ile başlayan) ve tüm komutlar dahil olmak üzere tüm dizinler arasında geçiş yapabilirsiniz:

for file in */ .*/ ; do echo "$file is a directory"; done

Sembolik linkleri hariç tutmak istiyorsanız:

for file in *; do 
  if [[ -d "$file" && ! -L "$file" ]]; then
    echo "$file is a directory"; 
  fi; 
done

not: listeyi kullanmak */ .*/bash olarak çalışır, fakat aynı zamanda klasörleri de gösterir .ve ..zsh sırasında bunları göstermez, fakat klasörde gizli bir dosya yoksa, bir hata atar.

Gizli dizinleri içerecek ve ../ hariç tutacak daha temiz bir sürüm dotglob seçeneğiyle olacak:

shopt -s dotglob
for file in */ ; do echo "$file is a directory"; done

(veya setopt dotglobzsh cinsinden)

ile dotglob unset yapabilirsiniz

shopt -u dotglob

2

Bu, listedeki her dizindeki tam yolu içerir:

for i in $(find $PWD -maxdepth 1 -type d); do echo $i; done

1
boşluk içeren klasörleri kullanırken sonları
Alejandro Sazo

0

Kullanım findile -execdizinlere döngü ve bir çağrı işlevi exec seçeneğinde:

dosomething () {
  echo "doing something with $1"
}
export -f dosomething
find -path './*' -prune -type d -exec bash -c 'dosomething "$0"' {} \;

Gizli dizinleri dahil etmek / hariç tutmak için shopt -s dotglobveya kullanınshopt -u dotglob


0

Bu, tüm dizinleri verilen bir yoldaki alt dizinlerin sayısı ile birlikte listeler:

for directory in */ ; do D=$(readlink -f "$directory") ; echo $D = $(find "$D" -mindepth 1 -type d | wc -l) ; done

-1
ls -d */ | while read d
do
        echo $d
done

This is one directory with spaces in the name- ama çoklu olarak ayrıştırılır.
Piskvor

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.