Как мне перебирать только каталоги в bash?

У меня есть папка с некоторыми каталогами и некоторыми файлами (некоторые скрыты, начиная с точки).

for d in *; do
 echo $d
done

будет циклически проходить через все файлы, но я хочу, чтобы цикл был только через каталоги. Как это сделать?

182 голоса | спросил rubo77 14 PM00000070000003731 2013, 19:43:37

12 ответов


16

Если вам нужно выбрать более конкретные файлы, чем только каталоги, используйте 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)
ответил rubo77 22 +04002013-10-22T08:50:03+04:00312013bEurope/MoscowTue, 22 Oct 2013 08:50:03 +0400 2013, 08:50:03
240

Вы можете указать косую черту в конце, чтобы соответствовать только каталогам:

for d in */ ; do
    echo "$d"
done
ответил choroba 14 PM00000070000004531 2013, 19:46:45
57

Вы можете протестировать с помощью -d:

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

Это один из операторов проверки файлов .

ответил goldilocks 14 PM00000070000002531 2013, 19:59:25
16

Остерегайтесь того, что решение choroba, хотя и элегантно, может вызвать неожиданное поведение, если в текущем каталоге отсутствуют каталоги. В этом состоянии, а не пропускании цикла 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 каталогом, а затем echo f, если условие возвращает true. Если f равен */, echo $f не будет выполняться.

ответил emagdne 27 J000000Monday15 2015, 02:54:47
10

Для этого вы можете использовать чистый bash, но лучше использовать find:

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

(find дополнительно будет включать скрытые каталоги)

ответил rush 14 PM00000070000005831 2013, 19:46:58
5

Это делается для поиска как видимых, так и скрытых каталогов в текущем рабочем каталоге, за исключением корневого каталога:

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

 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' и т. д.

ответил AsymLabs 21 +04002013-10-21T07:47:02+04:00312013bEurope/MoscowMon, 21 Oct 2013 07:47:02 +0400 2013, 07:47:02
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
ответил AsymLabs 21 +04002013-10-21T07:47:02+04:00312013bEurope/MoscowMon, 21 Oct 2013 07:47:02 +0400 2013, 07:47:02
3

Это будет включать полный путь в каждый каталог в списке:

for i in $(find $PWD -maxdepth 1 -type d); do echo $i; done
ответил user1529891 22 +04002013-10-22T09:16:54+04:00312013bEurope/MoscowTue, 22 Oct 2013 09:16:54 +0400 2013, 09:16:54
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, чтобы включить /исключить скрытые каталоги

ответил rubo77 22 +04002013-10-22T10:26:53+04:00312013bEurope/MoscowTue, 22 Oct 2013 10:26:53 +0400 2013, 10:26:53
0
ls -d */ | while read d
do
        echo $d
done
ответил dcat 27 J000000Monday15 2015, 04:55:50
0

Здесь перечислены все каталоги вместе с количеством подкаталогов в заданном пути:

for directory in */ ; do D=$(readlink -f "$directory") ; echo $D = $(find "$D" -mindepth 1 -type d | wc -l) ; done
ответил pabloa98 26 TueEurope/Moscow2017-12-26T21:33:14+03:00Europe/Moscow12bEurope/MoscowTue, 26 Dec 2017 21:33:14 +0300 2017, 21:33:14
-2
ls -l | grep ^d

или

ll | grep ^d

Вы можете установить его как псевдоним

ответил user250676 11 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowMon, 11 Sep 2017 21:46:46 +0300 2017, 21:46:46

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132