Як перебирати каталоги в 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(IMHO це гнучкіше, ніж /tmpбезпосередньо в findкоманді. У вас є лише одне місце, - cd, щоб змінити, якщо ви хочете, щоб в цій папці відбулося більше дій)

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

  • -type d шукає лише каталоги

  • -printf '%f\n друкує лише ім’я знайденої папки (плюс новий рядок) для кожного звернення.

Et voilà!


До речі: Зауважте, що всі відповіді знайдуть і приховані папки (тобто папки, починаючи з крапки)! Якщо цього не потрібно, додайте `-regex '\ ./ [^ \.]. *' 'Перед параметрами printf або exec.
Болдевін

1
Корисно - якщо вам потрібно виконати лише одну команду на кожен удар ;-) - Тільки у мене є кілька команд для виконання :-(.
Мартін

Немає проблем: Адаптуйте це рішення: 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

18
+1 - Навіщо турбуватися, findколи ви можете додати косу
рису

19
так for dir in */; do echo $dir; doneце і для каталогів у поточному каталозі.
Ehtesh Choudhury

41
Було б добре, якби вона могла містити пояснення, що dir=${dir%*/}і що echo ${dir##*/}робить.
Джеремі С.

11
Це тому, що змінна dir буде включати зворотну косу риску в кінці. Він просто видаляє його, щоб мати чисте ім’я. % видалить / в кінці. ## видалить / на початку. Це оператори для обробки підрядків.
sfratini

8
Якщо немає відповідності, цикл буде спрацьовувати /tmp/*/, було б розумно включити чек, щоб перевірити, чи існує каталог справді.
Jrs42

41

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

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

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


Іншою можливістю для 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

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)

Оригінальна публікація (не працює з пробілами)

Натхненний Boldewyn : Приклад циклу з findкомандою.

for D in $(find /path/to/dir/ -mindepth 1 -maxdepth 1 -type d) ; do
    echo $D ;
done


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

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

findКоманда на основі відповіді по Boldewyn. Додаткову інформацію про 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.