Jak przeglądać katalogi w systemie Linux?

168

Piszę skrypt w bashu na Linuksie i muszę przejść przez wszystkie nazwy podkatalogów w podanym katalogu. Jak mogę przeglądać te katalogi (i pomijać zwykłe pliki)?

Na przykład:
podany katalog /tmp/
czy zawiera następujące podkatalogi:/tmp/A, /tmp/B, /tmp/C

Chcę odzyskać A, B, C.

Erik Sapir
źródło

Odpowiedzi:

129
cd /tmp
find . -maxdepth 1 -mindepth 1 -type d -printf '%f\n'

Krótkie wyjaśnienie:

  • find znajduje pliki (dość oczywiste)

  • .jest bieżącym katalogiem, który po cdjest /tmp(IMHO jest to bardziej elastyczne niż posiadanie /tmpbezpośrednio w findpoleceniu. Masz tylko jedno miejsce cd, do zmiany, jeśli chcesz, aby więcej akcji było wykonywanych w tym folderze)

  • -maxdepth 1i -mindepth 1upewnij się, że findwygląda tylko w bieżącym katalogu i nie włącza .się do wyniku

  • -type d szuka tylko katalogów

  • -printf '%f\n wypisuje tylko nazwę znalezionego folderu (plus nowa linia) dla każdego trafienia.

Zrobione!

Boldewyn
źródło
Przy okazji: zwróć uwagę, że wszystkie odpowiedzi również znajdą ukryte foldery (czyli foldery zaczynające się od kropki)! Jeśli tego nie chcesz, dodaj `-regex '\ ./ [^ \.]. *' 'Przed opcjami printf lub exec.
Boldewyn
1
Pomocne - jeśli musisz wykonać tylko jedną komendę dla każdego uderzenia ;-) - Tylko ja mam kilka komend do wykonania :-(.
Martin
Żaden problem: dostosuj to rozwiązanie: transnum.blogspot.de/2008/11/… Wewnątrz while..donepętli możesz zwariować.
Boldewyn
1
To bardzo fajna metoda w niektórych przypadkach użycia; find„s -execopcja pozwala uruchamiać dowolne polecenie dla każdego pliku / katalogu.
jtpereyda
451

Wszystkie dotychczasowe odpowiedzi są używane find, więc tutaj jest tylko powłoka. Nie potrzebujesz zewnętrznych narzędzi w Twoim przypadku:

for dir in /tmp/*/     # list directories in the form "/tmp/dirname/"
do
    dir=${dir%*/}      # remove the trailing "/"
    echo ${dir##*/}    # print everything after the final "/"
done
ghostdog74
źródło
18
+1 - Po co zawracać sobie głowę tym, findkiedy możesz dołączyć ukośnik do symbolu wieloznacznego
Tobias Kienzler,
19
tak for dir in */; do echo $dir; donejest dla katalogów w bieżącym katalogu.
Ehtesh Choudhury
41
Byłoby miło, gdyby zawierało wyjaśnienie, co dir=${dir%*/}i echo ${dir##*/}robi.
Jeremy S.
11
Dzieje się tak, ponieważ zmienna dir będzie zawierała odwrotny ukośnik na końcu. Po prostu go usuwa, żeby mieć czyste imię. % usunie / na końcu. ## usunie / na początku. Są to operatory do obsługi podciągów.
sfratini
8
Jeśli nie ma dopasowania, pętla zostanie wyzwolona /tmp/*/, rozsądnie byłoby uwzględnić sprawdzenie, czy katalog rzeczywiście istnieje.
Jrs42
41

Możesz przeglądać wszystkie katalogi, w tym katalogi ukryte (zaczynając od kropki) za pomocą:

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

uwaga: użycie listy */ .*/działa w zsh tylko wtedy, gdy istnieje przynajmniej jeden ukryty katalog w folderze. W bash pokaże również .i..


Inną możliwością dołączenia przez bash ukrytych katalogów byłoby użycie:

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

Jeśli chcesz wykluczyć linki symboliczne:

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

Aby wyprowadzić tylko końcową nazwę katalogu (A, B, C zgodnie z pytaniami) w każdym rozwiązaniu, użyj tego w pętlach:

file="${file%/}"     # strip trailing slash
file="${file##*/}"   # strip path and leading slash
echo "$file is the directoryname without slashes"

Przykład (działa to również z katalogami zawierającymi spacje):

mkdir /tmp/A /tmp/B /tmp/C "/tmp/ dir with spaces"
for file in /tmp/*/ ; do file="${file%/}"; echo "${file##*/}"; done
rubo77
źródło
16

Działa z katalogami zawierającymi spacje

Zainspirowany Sorpigalem

while IFS= read -d $'\0' -r file ; do 
    echo $file; ls $file ; 
done < <(find /path/to/dir/ -mindepth 1 -maxdepth 1 -type d -print0)

Oryginalny post (nie działa ze spacjami)

Zainspirowany przez Boldewyn : Przykład pętli z findpoleceniem.

for D in $(find /path/to/dir/ -mindepth 1 -maxdepth 1 -type d) ; do
    echo $D ;
done
zpon
źródło
9
find . -mindepth 1 -maxdepth 1 -type d -printf "%P\n"
Ignacio Vazquez-Abrams
źródło
To też jest miłe i eliminuje potrzebę basename. Wolałbym to od mojej odpowiedzi.
Boldewyn
6

Najczęściej używam techniki find | xargs. Na przykład, jeśli chcesz, aby każdy plik w tym katalogu i wszystkie jego podkatalogi były czytelne dla wszystkich, możesz wykonać:

find . -type f -print0 | xargs -0 chmod go+r
find . -type d -print0 | xargs -0 chmod go+rx

-print0Opcja kończy się znakiem NULL zamiast przestrzeni. Ta -0opcja dzieli dane wejściowe w ten sam sposób. Więc to jest kombinacja do użycia w plikach ze spacjami.

Możesz sobie wyobrazić ten łańcuch poleceń jako pobierający wyjście każdego wiersza findi przyklejający go na końcu chmodpolecenia.

Jeśli polecenie, które chcesz uruchomić jako argument w środku zamiast na końcu, musisz być trochę kreatywny. Na przykład musiałem przejść do każdego podkatalogu i uruchomić polecenie latemk -c. Więc użyłem (z Wikipedii ):

find . -type d -depth 1 -print0 | \
    xargs -0 sh -c 'for dir; do pushd "$dir" && latexmk -c && popd; done' fnord

Ma to skutek for dir $(subdirs); do stuff; done, ale jest bezpieczne w przypadku katalogów ze spacjami w nazwach. Ponadto oddzielne wywołania stuffsą wykonywane w tej samej powłoce, dlatego w moim poleceniu musimy wrócić do bieżącego katalogu za pomocą popd.

Matthew Leingang
źródło
3

minimalna pętla bash, z której możesz zbudować (na podstawie odpowiedzi ghostdog74)

for dir in directory/*                                                     
do                                                                                 
  echo ${dir}                                                                  
done

spakować całą masę plików według katalogu

for dir in directory/*
do
  zip -r ${dir##*/} ${dir}
done                                               
Harry Moreno
źródło
Zawiera listę wszystkich plików directory, nie tylko podkatalogów.
dexteritas
2

find . -type d -maxdepth 1

Anonimowy
źródło
2
ta odpowiedź jest dobra, z wyjątkiem tego, że otrzymuję pełne ścieżki, podczas gdy chcę tylko nazwy katalogów.
Erik Sapir
2

Jeśli chcesz wykonać wiele poleceń w pętli for, możesz zapisać wynik findwith mapfile(bash> = 4) jako zmienną i przejść przez tablicę za pomocą ${dirlist[@]}. Działa również z katalogami zawierającymi spacje.

findPoleceń opiera się na odpowiedź przez Boldewyn. Więcej informacji na temat findpolecenia można znaleźć tam.

IFS=""
mapfile -t dirlist < <( find . -maxdepth 1 -mindepth 1 -type d -printf '%f\n' )
for dir in ${dirlist[@]}; do
    echo ">${dir}<"
    # more commands can go here ...
done
dexteritas
źródło