Як я переглядаю лише каталоги в bash?


249

У мене є папка з деякими каталогами та деякими файлами (деякі приховані, починаючи з крапки).

for d in *; do
 echo $d
done

буде проходити цикл через усі файли, але я хочу циклічно лише через каталоги. Як це зробити?

Відповіді:


331

Ви можете вказати косу рису в кінці, щоб відповідати лише каталогів:

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

7
Зауважте, що він також включає посилання на каталоги.
Стефан Шазелас

1
то як би ви тоді виключили посилання?
rubo77

3
@ rubo77: Ви можете перевірити, [[ -L $d ]]чи є $ d символічним посиланням.
choroba

1
@AsymLabs Це неправильно, set -Pвпливає лише на команди, які змінюють каталог. sprunge.us/TNac
Chris Down

4
@choroba: [[ -L "$f" ]]не буде виключати символьних посилань у цьому випадку */, вам потрібно зняти [[ -L "${f%/}" ]]
проділ

78

Ви можете протестувати за допомогою -d:

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

Це один із операторів тестування файлів .


8
Зауважте, що вона буде містити посилання на каталоги.
Стефан Шазелас

21
if [[ -d "$f" && ! -L "$f" ]]буде виключати символьні посилання
rubo77

Перерветься на порожній каталог. if [[ "$f" = "*" ]]; then continue; fi
Пісквор

30

Остерігайтеся, що рішення хороби, хоч і елегантне, може викликати несподіване поведінку, якщо в поточному каталозі відсутні каталоги. У такому стані, замість того, щоб пропускати forцикл, bash запустить цикл рівно один раз там, де dдорівнює */:

#!/usr/bin/env bash

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

Для захисту від цієї справи рекомендую використовувати наступне:

#!/usr/bin/env bash

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

Цей код буде проглядати всі файли в поточному каталозі, перевіряти, чи fє каталог, а потім лунає, fякщо умова повертається як справжня. Якщо fдорівнює */, echo $fне виконується.


3
Набагато простіше shopt -s nullglob.
choroba

21

Якщо вам потрібно вибрати більш конкретні файли, ніж використовуються лише каталоги findта передайте їх у while read:

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

Використовуйте shopt -u dotglobдля виключення прихованих каталогів (або setopt dotglob/ unsetopt dotglobв zsh).

IFS=щоб уникнути розщеплення імен файлів, що містять один із $IFS, наприклад:'a b'

див. відповідь AsymLabs нижче для отримання додаткових findваріантів


редагувати:
Якщо вам потрібно створити значення виходу з циклу while, ви можете обійти додаткову нижню частину за допомогою цього фокусу:

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

2
Я знайшов це рішення на: stackoverflow.com/a/8489394/1069083
rubo77

11

Ви можете використовувати чистий bash для цього, але краще використовувати find:

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

(пошук додатково буде включати приховані каталоги)


8
Зауважте, що він не включає символьні посилання на каталоги. Ви можете використовувати shopt -s dotglobдля bashвключення прихованих каталогів. Ваше також включить .. Також зауважте, що -maxdepthце не стандартний варіант ( -pruneє).
Стефан Шазелас

2
Цей dotglobваріант цікавий, але dotglobстосується лише використання *. find .завжди буде містити приховані каталоги (і поточний
редактор

6

Це робиться для пошуку як видимих, так і прихованих каталогів у поточному робочому каталозі, виключаючи кореневий каталог:

просто перебирати каталоги:

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

включити в результат символьні посилання:

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

зробити що-небудь до кожного каталогу (за винятком символьних посилань):

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

щоб виключити приховані каталоги:

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

виконати кілька команд на повернених значеннях (дуже надуманий приклад):

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

замість 'sh -c' також можна використовувати 'bash -c' тощо.


Що станеться, якщо ехо '* /' в 'для d в ехо * /' містить, скажімо, 60 000 каталогів?
AsymLabs

Ви отримаєте помилку "Забагато файлів", але це можна вирішити: Як обійти "Забагато відкритих файлів" у debian
rubo77

1
Приклад xargs працює лише для підмножини імен каталогів 9e.g. ті, які мають пробіли чи нові рядки, не працюватимуть). Краще використовувати, ... -print0 | xargs -0 ...якщо ви не знаєте, які точні назви.
Антон

1
@ rubo77 додав спосіб виключення прихованих файлів / каталогів та виконання декількох команд - звичайно, це можна зробити і зі скриптом.
AsymLabs

1
Я додав приклад у своїй відповіді внизу, як зробити що-небудь до кожного каталогу за допомогою функції зfind * | while read file; do ...
rubo77

3

Ви можете прокручувати всі каталоги, включаючи приховані каталоги (починаючи з крапки) в одному рядку та кілька команд за допомогою:

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

Якщо ви хочете виключити посилання:

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

Примітка: використання списку */ .*/працює в bash, але також відображає папки, .і, ..хоча в zsh, вони не показуватимуть їх, але видають помилку, якщо в папці немає прихованого файла

Більш чиста версія, яка буде включати приховані каталоги та виключати ../, буде з опцією dotglob:

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

(або setopt dotglobв zsh)

ви можете відключити dotglob за допомогою

shopt -u dotglob

2

Це буде включати повний шлях до кожного каталогу у списку:

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

1
перерви при використанні папок із пробілами
Алехандро Сазо

0

Використовуйте findдля, -execщоб переглядати каталоги та викликати функцію в опції exec:

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

Використовувати shopt -s dotglobабо shopt -u dotglobвключати / виключати приховані каталоги


0

Тут перераховані всі каталоги разом із кількістю підкаталогів на певному шляху:

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


-5
ls -l | grep ^d

або:

ll | grep ^d

Ви можете встановити його як псевдонім


1
На жаль, я не думаю, що це відповідає на питання, яке було "Я хочу циклічно лише через каталоги" - дещо інше, ніж ця lsвідповідь на основі, в якій перераховані каталоги.
Jeff Schaller

1
Ніколи не годиться проаналізувати вихід ls: mywiki.wooledge.org/ParsingLs
codeforester
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.