Wie kann ich nur Verzeichnisse in Bash durchlaufen?


249

Ich habe einen Ordner mit einigen Verzeichnissen und einigen Dateien (einige sind versteckt, beginnend mit Punkt).

for d in *; do
 echo $d
done

Alle Dateien werden durchlaufen, aber ich möchte nur Verzeichnisse durchlaufen. Wie mache ich das?

Antworten:


332

Sie können einen Schrägstrich am Ende angeben, um nur Verzeichnisse zu finden:

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

7
Beachten Sie, dass es auch Symlinks zu Verzeichnissen enthält.
Stéphane Chazelas

1
Wie würden Sie dann Symlinks ausschließen?
Rubo77

3
@ rubo77: Du kannst testen, [[ -L $d ]]ob $ d ein symbolischer Link ist.
Choroba

1
@AsymLabs Das ist falsch, set -Pbetrifft nur Befehle, die das Verzeichnis wechseln. sprunge.us/TNac
Chris Down

4
@choroba: [[ -L "$f" ]]Symlinks werden in diesem Fall nicht mit ausgeschlossen */, Sie müssen den abschließenden Schrägstrich mit [[ -L "${f%/}" ]]
entfernen


30

Beachten Sie, dass die elegante Lösung von choroba unerwartetes Verhalten hervorrufen kann, wenn im aktuellen Verzeichnis keine Verzeichnisse verfügbar sind. In diesem Zustand wird die forSchleife nicht übersprungen, sondern genau einmal ausgeführt, wobei dgilt */:

#!/usr/bin/env bash

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

Ich empfehle Folgendes, um sich vor diesem Fall zu schützen:

#!/usr/bin/env bash

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

Dieser Code durchläuft alle Dateien im aktuellen Verzeichnis, überprüft, ob fes sich um ein Verzeichnis handelt, und gibt ein Echo aus, fwenn die Bedingung wahr ist. Wenn fgleich */, echo $fwird nicht ausgeführt.


3
Viel einfacher zu shopt -s nullglob.
Choroba

21

Wenn Sie spezifischere Dateien als nur Verzeichnisse auswählen müssen, verwenden Sie diese findund übergeben Sie sie an while read:

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

Verwenden Sie shopt -u dotglobdiese Option, um versteckte Verzeichnisse (oder setopt dotglob/ unsetopt dotglobin zsh) auszuschließen .

IFS=Um zu vermeiden, dass Dateinamen aufgeteilt werden, die eine der folgenden Angaben enthalten $IFS:'a b'

siehe AsymLabs beantworten unten für weitere findOptionen


edit:
Falls Sie einen Exit-Wert innerhalb der while-Schleife erstellen müssen, können Sie die zusätzliche Subshell mit folgendem Trick umgehen:

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

2
Ich fand diese Lösung auf: stackoverflow.com/a/8489394/1069083
rubo77

11

Sie können dafür reines bash verwenden, aber es ist besser, find zu verwenden:

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

(find schließt zusätzlich versteckte Verzeichnisse ein)


8
Beachten Sie, dass Symlinks zu Verzeichnissen nicht enthalten sind. Sie können shopt -s dotglobfor verwenden bash, um versteckte Verzeichnisse einzuschließen. Ihr wird auch enthalten .. Beachten Sie auch, dass dies -maxdepthkeine Standardoption -pruneist.
Stéphane Chazelas

2
Die dotglobOption ist interessant, dotglobgilt aber nur für die Verwendung von *. find .wird immer versteckte Verzeichnisse (und das aktuelle Verzeichnis auch) enthalten
rubo77

6

Dies geschieht, um sowohl sichtbare als auch versteckte Verzeichnisse im aktuellen Arbeitsverzeichnis zu finden, mit Ausnahme des Stammverzeichnisses:

So durchlaufen Sie Verzeichnisse:

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

Symlinks in das Ergebnis aufnehmen:

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

Um etwas mit jedem Verzeichnis zu tun (außer Symlinks):

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

versteckte Verzeichnisse ausschließen:

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

So führen Sie mehrere Befehle für die zurückgegebenen Werte aus (ein sehr ausgeklügeltes Beispiel):

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

Anstelle von 'sh -c' kann auch 'bash -c' usw. verwendet werden.


Was passiert, wenn das Echo '* /' in 'für d in echo * /' beispielsweise 60.000 Verzeichnisse enthält?
AsymLabs

Sie werden den Fehler "Zu viele Dateien" erhalten, der jedoch behoben werden kann: So umgehen Sie "Zu viele offene Dateien" in debian
rubo77

1
Das xargs-Beispiel funktioniert nur für eine Teilmenge der Verzeichnisnamen 9e.g. diejenigen mit Leerzeichen oder Zeilenumbrüchen würden nicht funktionieren). Besser zu verwenden, ... -print0 | xargs -0 ...wenn Sie die genauen Namen nicht kennen.
Anthon

1
@ rubo77 hinzugefügt, um versteckte Dateien / Verzeichnisse und die Ausführung mehrerer Befehle auszuschließen - dies kann natürlich auch mit einem Skript erfolgen.
AsymLabs

1
Ich habe in meiner Antwort unten ein Beispiel hinzugefügt, wie man mit einer Funktion mitfind * | while read file; do ...
rubo77 22.10.13

3

Sie können alle Verzeichnisse einschließlich versteckter Verzeichnisse (beginnend mit einem Punkt) in einer Zeile und mit mehreren Befehlen durchlaufen:

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

Wenn Sie Symlinks ausschließen möchten:

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

Hinweis: die Liste mit */ .*/Arbeiten in der Bash, sondern zeigt auch die Ordner .und ..während in zsh wird es nicht diese zeigt aber einen Fehler aus , wenn es keine versteckte Datei in dem Ordner ist

Eine sauberere Version, die versteckte Verzeichnisse enthält und ../ ausschließt, enthält die Option dotglob:

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

(oder setopt dotglobin zsh)

Sie können dotglob mit deaktivieren

shopt -u dotglob

2

Dies wird den vollständigen Pfad in jedem Verzeichnis in der Liste enthalten:

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

1
Pausen bei der Verwendung von Ordnern mit Leerzeichen
Alejandro Sazo

0

Verwenden Sie findmit -exec, um die Verzeichnisse zu durchlaufen und eine Funktion in der Option exec aufzurufen :

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

Verwenden Sie shopt -s dotgloboder shopt -u dotglob, um versteckte Verzeichnisse ein- oder auszuschließen


0

Dies listet alle Verzeichnisse zusammen mit der Anzahl der Unterverzeichnisse in einem bestimmten Pfad auf:

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- aber wird als mehrere analysiert.
Piskvor

-5
ls -l | grep ^d

oder:

ll | grep ^d

Sie können es als Alias ​​festlegen


1
Leider glaube ich nicht, dass dies die Frage beantwortet, die lautete: "Ich möchte nur Verzeichnisse durchlaufen" - und die sich geringfügig von dieser lsAntwort unterscheidet, in der Verzeichnisse aufgelistet sind .
Jeff Schaller

1
Es ist nie eine gute Idee , die Ausgabe zu analysieren ls: mywiki.wooledge.org/ParsingLs
codeforester
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.