Каталоги с двумя или более файлами

11

Я хочу найти подкаталог текущего каталога, который (то есть подкаталог) содержит 2 или более обычных файлов.

Меня не интересуют каталоги, содержащие менее 2 файлов, а также каталоги, которые содержат только подкаталоги.

Porton
источник

Ответы:

12

Здесь совершенно другой подход, основанный на GNU findи uniq. Это намного быстрее и намного проще для процессора, чем ответы, основанные на выполнении команды оболочки, которая считает файлы для каждого найденного каталога.

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

Команда findпечатает каталог всех файлов в иерархии и uniqотображает только те каталоги, которые появляются как минимум дважды.

xhienne
источник
2
Вы не должны анализировать вывод find. В этом случае, потому что GNU findбудет искажать имена каталогов, в которых есть символы, которые нельзя распечатать в текущей локали (например, «ä» в локали C). См. Также unix.stackexchange.com/questions/321697/…
Кусалананда
4
@ Kusalananda, а не тогда, когда вывод не переходит в tty. Здесь единственная проблема - с символами перевода строки, которые можно исправить с помощью-printf '%h\0' | sort -z | uniq -zd | xargs -r0 ...
Стефан
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 ]

Этот скрипт будет подсчитывать количество обычных файлов (пропуская символические ссылки) в каталоге, указанном в качестве первого аргумента командной строки (from 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 Я рассмотрел скрытые файлы и добавлю заметку об этом. Нет ошибки, если в каталоге нет обычных файлов, так [ "" -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
αғsнιη
источник
У меня это тоже было сначала, но у вас будут проблемы с каталогами, содержащими несколько подкаталогов и файлов. Он также не отсеивает каталоги, содержащие только подкаталоги.
Кусалананда
Это на самом деле не решает. Он находит оба testи те dir2каталоги , в моей тестовой конфигурации (см мой ответ).
Кусалананда
Работает для вашего примера, но добавляет test/x1и в test/x2виде файлов ... $1и $2будет каталогами для test, и каталог будет пропущен.
Кусалананда
@Kusalananda Я не нашел ничего, кроме того, что вы ответили. Я пытался изменить какую-то часть своей команды, чтобы она не была точной копией вашей (я не исключал скрытых файлов, как вы сделали), мои извинения.
αғsнιη
1
Не беспокойтесь :-)
Кусалананда
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 или более обычных файла

RomanPerekhrest
источник