Каталоги з двома або більше файлами


11

Я хочу знайти підкаталог поточного каталогу, який (тобто цей підкаталог) містить 2 або більше регулярних файлів.

Мене не цікавлять каталоги, що містять менше 2 файлів, а також каталоги, які містять лише підкаталоги.

Відповіді:


12

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

find . -type f -printf '%h\n' | sort | uniq -d

findКоманда друкує каталог всіх файлів в ієрархії і uniqвідображає тільки ті каталоги , які з'являються принаймні в два рази.


2
Ви не повинні розбирати вихід find. У цьому випадку, оскільки GNU findбуде маніпулювати іменами каталогів, які містять символи, які не можна друкувати в поточній локалі (наприклад, "ä" у мові C). Дивіться також unix.stackexchange.com/questions/321697/…
Kusalananda

4
@Kusalananda, не тоді, коли вихід не надходить на tty. Тут єдина проблема з символами нового рядка, які ви можете виправити, скориставшись-printf '%h\0' | sort -z | uniq -zd | xargs -r0 ...
Stéphane Chazelas

6
find . -type d \
    -exec sh -c 'c=0; for n in "$1"/*; do [ -f "$n" ] && [ ! -h "$n" ] && c=$(( c + 1 )); done; [ "$c" -ge 2 ]' sh {} ';' \
    -print

Це знайде всі імена в поточному каталозі або під ним, а потім відфільтрує всі імена, які не є іменами каталогів.

Решта імен каталогів буде надана цьому короткому сценарію:

c=0
for n in "$1"/*; do
    [ -f "$n" ] && [ ! -h "$n" ] && c=$(( c + 1 ))
done

[ "$c" -ge 2 ]

Цей скрипт буде рахувати кількість регулярних файлів (пропуск символічних посилань) у каталозі, вказаний як перший аргумент командного рядка (від find). Остання команда в скрипті - це тест, щоб перевірити, чи було число 2 або більше. Результатом цього тесту є повернене значення (статус виходу) сценарію.

Якщо тест вдався, -printвиведе findдрук шляху до каталогу.

Щоб також розглянути приховані файли (файли, імена яких починаються з крапки), змініть sh -cсценарій, не кажучи

for n in "$1"/*; do

до

for n in "$1"/* "$1"/.*; do

Тестування:

$ tree
.
`-- test
    |-- a
    |-- dir1
    |   |-- a
    |   |-- b
    |   `-- c
    `-- dir2
        |-- dira
        |-- dirb
        |   |-- file-1
        |   `-- file-2
        `-- dirc

6 directories, 6 files

$ find . -type d -exec sh -c 'c=0; for n in "$1"/*; do [ -f "$n" ] && [ ! -h "$n" ] && c=$(( c + 1 )); done; [ "$c" -ge 2 ]' sh {} ';' -print
./test/dir1
./test/dir2/dirb

Ваше рішення не враховує файли, назви яких починаються з крапки. Також слід ініціалізувати c = 0, щоб уникнути повідомлень про помилки з каталогами, які не містять жодного файлу.
xhienne

@xhienne Я розглядав приховані файли і додаю про це примітку. Немає помилок, якщо в каталозі немає регулярних файлів, оскільки [ "" -ge 2 ]це тест.
Кусалаланда

Не впевнений, як ви визначаєте "дійсний". POSIX вимагає, щоб arg1 було цілим числом. dash, bash --posixі testвсі відображатимуть повідомлення про помилку та виходять з 2 (тобто "Виникла помилка")
xhienne

@xhienne Ах, я тестував систему, яка kshпрацює як sh. Буде негайно внесено зміни. Дякую, що тикаєш на мене! :-)
Кусалаланда

Також [ -f ... ]відміни символічні посилання. Ви повинні додати тест для їх усунення, оскільки в питанні вказано, що слід враховувати лише звичайні файли.
xhienne

6

За допомогою відповіді Жилла про SU та його зворотному напрямку та деякій модифікації, ось що вам потрібно.

find . -type d -exec sh -c 'set -- "$1"/*;X=0; 
    for args; do [ -f "$args" ] && X=$((X+1)) ;done; [ "$X" -gt 1 ] ' _ {} \; -print

Дерево каталогів.

.
├── test
│   ├── dir1
│   │   ├── a
│   │   ├── b
│   │   └── c
│   ├── dir2
│   │   ├── dira
│   │   │   └── a file\012with\012multiple\012line
│   │   ├── dirb
│   │   │   ├── file-1
│   │   │   └── file-2
│   │   └── dirc
│   ├── diraa
│   ├── dirbb
│   ├── dircc
│   └── x
│   └── x1
│   └── x2
└── test2
    ├── dir3
    └── dir4

Результат:

./test
./test/dir1
./test/dir2/dirb

У мене це було і спочатку, але у вас виникнуть проблеми з каталогами, що містять кілька підкаталогів і файлів. Він також не відсіває каталоги, що містять лише підкаталоги.
Кусалаланда

Це насправді не вирішує. Він знаходить і те, testі dir2каталоги в моїй тестовій установці (див. Мою відповідь).
Кусалаланда

Працює для вашого прикладу, але додайте test/x1і test/x2як файли ..., $1і $2вони будуть каталогіми test, і каталог буде пропущений.
Kusalananda

@Kusalananda Жодного способу я не знайшов, окрім того, що ти відповів, я намагався змінити частину моєї команди, щоб не бути точним дублікатом твоїх (я не виключав приховані файли, як ти), мої вибачення.
αғsnιη

1
Не хвилюйтеся :-)
Kusalananda

3

Ще один find+ wcпідхід:

find path/currdir -maxdepth 1 -type d ! -empty ! -path "path/currdir" \
-exec sh -c 'count=$(find "$1" -maxdepth 1 -type f | wc -l); [ $count -ge 2 ]' _ {} \; -print

  • path/currdir - шлях до поточного каталогу

  • -maxdepth 1- розглядати лише прямі дочірні папки

  • ! -empty - ігноруйте порожні підпапки

  • ! -path "path/currdir" - ігнорувати поточний шлях до каталогу

  • count=$(find "$1" -maxdepth 1 -type f | wc -l)- countпризначається з кількістю файлів для кожної знайденої підпапки

  • [ $count -ge 2 ] ... -print - друкувати назву / шлях підпапки, що містить 2 або більше регулярних файлів

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.