Jak przeglądać tylko katalogi w bash?

249

Mam folder z niektórymi katalogami i niektórymi plikami (niektóre są ukryte, zaczynając od kropki).

for d in *; do
 echo $d
done

zapętli wszystkie pliki, ale chcę zapętlić tylko katalogi. W jaki sposób mogę to zrobić?

rubo77
źródło

Odpowiedzi:

331

Możesz określić ukośnik na końcu, aby pasował tylko do katalogów:

for d in */ ; do
    echo "$d"
done
choroba
źródło
7
Zauważ, że zawiera także dowiązania symboliczne do katalogów.
Stéphane Chazelas
1
jak więc wykluczyłbyś dowiązania symboliczne?
rubo77
3
@ rubo77: Możesz sprawdzić, [[ -L $d ]]czy $ d jest dowiązaniem symbolicznym.
choroba
1
@AsymLabs Niepoprawne, set -Pwpływa tylko na polecenia zmieniające katalog. sprunge.us/TNac
Chris Down
4
@choroba: [[ -L "$f" ]]nie wyklucza dowiązań symbolicznych w tym przypadku za pomocą */, musisz rozebrać ukośnik końcowy za pomocą [[ -L "${f%/}" ]](patrz Test linku z ukośnikiem końcowym )
rubo77
78

Możesz przetestować za pomocą -d:

for f in *; do
    if [ -d "$f" ]; then
        # $f is a directory
    fi
done

Jest to jeden z operatorów testowania plików .

Złotowłosa
źródło
8
Zauważ, że będzie zawierać dowiązania symboliczne do katalogów.
Stéphane Chazelas
21
if [[ -d "$f" && ! -L "$f" ]]wykluczy dowiązania symboliczne
rubo77
Zepsuje się w pustym katalogu. if [[ "$f" = "*" ]]; then continue; fi
Piskvor
30

Uważaj, że rozwiązanie tej choroby, choć eleganckie, może wywołać nieoczekiwane zachowanie, jeśli w bieżącym katalogu nie ma dostępnych katalogów. W tym stanie, zamiast pomijania forpętli, bash uruchomi pętlę dokładnie raz, gdzie djest równa */:

#!/usr/bin/env bash

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

Zalecam użycie następujących elementów w celu ochrony przed tym przypadkiem:

#!/usr/bin/env bash

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

Ten kod będzie przechodził przez wszystkie pliki w bieżącym katalogu, sprawdza, czy fjest to katalog, a następnie echo, fjeśli warunek zwraca wartość true. Jeśli fjest równe */, echo $fnie zostanie wykonane.

emagdne
źródło
3
O wiele łatwiej shopt -s nullglob.
choroba
21

Jeśli musisz wybrać bardziej szczegółowe pliki, użyj tylko katalogów findi przekaż je do while read:

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

Służy shopt -u dotglobdo wykluczania ukrytych katalogów (lub setopt dotglob/ unsetopt dotglobw zsh).

IFS=aby uniknąć podziału nazw plików zawierających jeden z $IFS, na przykład:'a b'

zobacz AsymLabs odpowiedzieć poniżej więcej findopcji


edycja:
Jeśli chcesz utworzyć wartość wyjściową z pętli while, możesz obejść dodatkową podpowłokę za pomocą tej sztuczki:

while IFS= read -r d; do 
    if [ "$d" == "something" ]; then exit 1; fi
done < <(find * -prune -type d)
rubo77
źródło
2
Znalazłem to rozwiązanie na: stackoverflow.com/a/8489394/1069083
rubo77
11

Możesz użyć do tego czystej bash, ale lepiej użyć find:

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

(znajdź dodatkowo będzie zawierać ukryte katalogi)

wysypka
źródło
8
Zauważ, że nie zawiera dowiązań symbolicznych do katalogów. Możesz użyć shopt -s dotglobdo, bashaby dołączyć ukryte katalogi. Twój będzie również obejmować .. Należy również pamiętać, że -maxdepthnie jest to standardowa opcja ( -prunejest).
Stéphane Chazelas,
2
dotglobOpcja jest interesująca, ale dotglobodnosi się wyłącznie do wykorzystania *. find .zawsze będzie zawierać ukryte katalogi (a także bieżący katalog)
rubo77
6

Odbywa się to w celu znalezienia zarówno widocznych, jak i ukrytych katalogów w bieżącym katalogu roboczym, z wyłączeniem katalogu głównego:

aby po prostu przeglądać katalogi:

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

w celu dołączenia dowiązań symbolicznych w wyniku:

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

zrobić coś z każdym katalogiem (z wyłączeniem dowiązań symbolicznych):

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

aby wykluczyć ukryte katalogi:

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

aby wykonać wiele poleceń na zwróconych wartościach (bardzo przemyślany przykład):

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

zamiast „sh -c” można również użyć „bash -c” itp.

AsymLabs
źródło
Co się stanie, jeśli echo „* /” in ”dla d in echo * /” zawiera, powiedzmy, 60 000 katalogów?
AsymLabs
Pojawi się błąd „Zbyt wiele plików”, ale można to rozwiązać: Jak obejść „Zbyt wiele otwartych plików” w debian
rubo77
1
Przykład xargs działa tylko dla podzbioru nazw katalogów 9e.g. te ze spacjami lub znakami nowej linii nie będą działać). Lepiej używać, ... -print0 | xargs -0 ...jeśli nie wiesz, jakie są dokładne nazwy.
Anthon
1
@ rubo77 dodał sposób wykluczania ukrytych plików / katalogów i wykonywania wielu poleceń - oczywiście można to zrobić również za pomocą skryptu.
AsymLabs
1
Dodałem przykład w mojej odpowiedzi na dole, jak zrobić coś do każdego katalogu za pomocą funkcji zfind * | while read file; do ...
rubo77
3

Możesz przewijać wszystkie katalogi, w tym ukryte katalogi (zaczynające się od kropki) w jednym wierszu i wiele poleceń za pomocą:

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

Jeśli chcesz wykluczyć dowiązania symboliczne:

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

Uwaga: za pomocą listy */ .*/działa w bash, ale również wyświetla foldery, .a ..jednocześnie w zsh nie pokaże tych, ale wygeneruje błąd, jeśli nie ma ukryty plik w folderze

Czystsza wersja, która będzie zawierać ukryte katalogi i wykluczać ../ będzie dostępna z opcją dotglob:

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

(lub setopt dotglobw Zsh)

możesz rozbroić dotglob za pomocą

shopt -u dotglob
rubo77
źródło
2

Obejmuje to pełną ścieżkę do każdego katalogu na liście:

for i in $(find $PWD -maxdepth 1 -type d); do echo $i; done
rubo77
źródło
1
pęka przy korzystaniu z folderów ze spacjami
Alejandro Sazo
0

Użyj za findpomocą, -execaby przeglądać katalogi i wywoływać funkcję w opcji exec:

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

Użyj shopt -s dotgloblub, shopt -u dotglobaby włączyć / wyłączyć ukryte katalogi

rubo77
źródło
0

Zawiera listę wszystkich katalogów wraz z liczbą podkatalogów w danej ścieżce:

for directory in */ ; do D=$(readlink -f "$directory") ; echo $D = $(find "$D" -mindepth 1 -type d | wc -l) ; done
pabloa98
źródło
-1
ls -d */ | while read d
do
        echo $d
done
dcat
źródło
This is one directory with spaces in the name- ale zostanie przeanalizowany jako wielokrotność.
Piskvor
-5
ls -l | grep ^d

lub:

ll | grep ^d

Możesz ustawić go jako alias

użytkownik250676
źródło
1
Niestety, nie sądzę, że to odpowiada na pytanie, które brzmiało: „Chcę zapętlać tylko przez katalogi” - co nieco różni się od lsodpowiedzi opartej na tej liście , która zawiera katalogi.
Jeff Schaller
1
Przetwarzanie danych wyjściowych ls: mywiki.wooledge.org/ParsingLs
codeforester