Как зациклить каталоги в Linux?

168

Я пишу скрипт на bash для Linux и мне нужно пройти через все имена подкаталогов в данном каталоге. Как я могу просмотреть эти каталоги (и пропустить обычные файлы)?

Например:
данный каталог /tmp/
имеет следующие подкаталоги:/tmp/A, /tmp/B, /tmp/C

Я хочу получить A, B, C.

Эрик Сапир
источник

Ответы:

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

Краткое объяснение:

  • find находит файлы (вполне очевидно)

  • .является текущим каталогом, который после cdявляется /tmp(ИМХО это более гибко, чем /tmpнепосредственно в findкоманде. У вас есть только одно место, которое cd, чтобы изменить, если вы хотите, чтобы в этой папке выполнялось больше действий)

  • -maxdepth 1и -mindepth 1убедитесь, что findсмотрит только в текущем каталоге и не включает .себя в результат

  • -type d ищет только каталоги

  • -printf '%f\n печатает только имя найденной папки (плюс символ новой строки) для каждого нажатия.

И вуаля!

Boldewyn
источник
Кстати, обратите внимание, что во всех ответах найдутся и скрытые папки (то есть папки, начинающиеся с точки)! Если вы не хотите этого, добавьте `-regex '\ ./ [^ \.]. *'` Перед параметрами printf или exec.
Болдевин
1
Полезно - если вам нужно выполнить только одну команду для каждого удара ;-) - Только у меня есть несколько команд для выполнения :-(.
Martin
Нет проблем: адаптируйте это решение: transnum.blogspot.de/2008/11/… Внутри while..doneцикла вы можете сходить с ума.
Болдевин
1
Это очень хороший метод для некоторых случаев использования; Опция find's -execпозволяет вам запустить любую команду для каждого файла / каталога.
Jtpereyda
451

Все ответы до сих пор используют find, так что вот один только с оболочкой. Нет необходимости во внешних инструментах в вашем случае:

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
источник
18
+1 - Зачем беспокоиться, findкогда вы можете добавить косую черту в подстановочный знак
Тобиас Кинцлер
19
так for dir in */; do echo $dir; doneже для каталогов в текущем каталоге.
Этеш Чоудхури
41
Было бы неплохо, если бы он мог содержать объяснение того, что dir=${dir%*/}и echo ${dir##*/}делает.
Джереми С.
11
Это потому, что переменная dir в конце будет содержать обратную косую черту. Он просто удаляет это, чтобы иметь чистое имя. % удалит / в конце. ## удалит / в начале. Это операторы для обработки подстрок.
Сфратини
8
Если совпадений нет, цикл будет запущен /tmp/*/, было бы целесообразно включить проверку, чтобы увидеть, существует ли каталог на самом деле.
Jrs42
41

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

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

примечание: использование списка */ .*/работает в zsh, только если в папке есть хотя бы один скрытый каталог. В bash это покажет также .и..


Другая возможность для bash включить скрытые каталоги - использовать:

shopt -s dotglob;
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

Чтобы вывести только конечное имя каталога (A, B, C в виде вопроса) в каждом решении, используйте это в циклах:

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

Пример (это также работает с каталогами, которые содержат пробелы):

mkdir /tmp/A /tmp/B /tmp/C "/tmp/ dir with spaces"
for file in /tmp/*/ ; do file="${file%/}"; echo "${file##*/}"; done
rubo77
источник
16

Работает с каталогами, которые содержат пробелы

Вдохновленный Сорпигалом

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

Оригинальный пост (не работает с пробелами)

Вдохновлен Болдевином : пример цикла с findкомандой.

for D in $(find /path/to/dir/ -mindepth 1 -maxdepth 1 -type d) ; do
    echo $D ;
done
zpon
источник
9
find . -mindepth 1 -maxdepth 1 -type d -printf "%P\n"
Игнасио Васкес-Абрамс
источник
Это тоже приятно и избавляет от необходимости basename. Я бы предпочел это, чем мой ответ.
Болдевин
6

Чаще всего я использую технику find | xargs. Например, если вы хотите сделать каждый файл в этом каталоге и все его подкаталоги общедоступными, вы можете сделать следующее:

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

-print0Вариант заканчивается нулевым символом , а не пространства. -0Вариант разделяет его вход таким же образом. Так что это комбинация для использования с файлами с пробелами.

Вы можете изобразить эту цепочку команд как вывод каждой строки, findвставив ее в конец chmodкоманды.

Если команда, которую вы хотите запустить в качестве аргумента в середине, а не в конце, вы должны быть немного креативными. Например, мне нужно было перейти в каждый подкаталог и запустить команду latemk -c. Поэтому я использовал (из Википедии ):

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

Это имеет эффект for dir $(subdirs); do stuff; done, но безопасно для каталогов с пробелами в их именах. Кроме того, отдельные вызовы stuffвыполняются в одной и той же оболочке, поэтому в моей команде мы должны вернуться в текущий каталог с помощью popd.

Мэтью Лейнган
источник
3

минимальный цикл bash, из которого вы можете построить (основанный на ответе ghostdog74)

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

сжать целую кучу файлов по каталогу

for dir in directory/*
do
  zip -r ${dir##*/} ${dir}
done                                               
Гарри Морено
источник
Здесь перечислены все файлы directory, а не только подкаталоги.
dexteritas
2

find . -type d -maxdepth 1

анонимное
источник
2
этот ответ хорош, за исключением того, что я получаю полные пути, а мне нужны только имена каталогов.
Эрик Сапир
2

Если вы хотите выполнить несколько команд в цикле for, вы можете сохранить результат findс помощью mapfile(bash> = 4) в качестве переменной и просмотреть массив с помощью ${dirlist[@]}. Он также работает с каталогами, содержащими пробелы.

Команда findоснована на ответе Болдевина. Дополнительную информацию о findкоманде можно найти там.

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
источник