Я хочу знать, сколько обычных файлов имеют расширение .c
в большой сложной структуре каталогов, а также сколько каталогов распределено по этим файлам. Я хочу получить только эти два числа.
Я видел этот вопрос о том, как узнать количество файлов, но мне нужно знать количество каталогов, в которых находятся файлы.
- Мои имена файлов (включая каталоги) могут содержать любые символы; они могут начинаться с
.
или-
иметь пробелы или переводы строк. - У меня могут быть некоторые символические ссылки, имена которых заканчиваются
.c
, и символические ссылки на каталоги. Я не хочу, чтобы символические ссылки отслеживались или учитывались, или я, по крайней мере, хочу знать, когда и когда они подсчитываются. - Структура каталогов имеет много уровней, и каталог верхнего уровня (рабочий каталог) содержит по крайней мере один
.c
файл.
Я поспешно написал некоторые команды в оболочке (Bash), чтобы подсчитать их сам, но я не думаю, что результат точен ...
shopt -s dotglob
shopt -s globstar
mkdir out
for d in **/; do
find "$d" -maxdepth 1 -type f -name "*.c" >> out/$(basename "$d")
done
ls -1Aq out | wc -l
cat out/* | wc -l
Это приводит к жалобам на неоднозначные перенаправления, пропускает файлы в текущем каталоге и отключает специальные символы (например, перенаправленный find
вывод печатает новые строки в именах файлов ) и записывает целую кучу пустых файлов (упс).
Как я могу надежно перечислить мои .c
файлы и содержащиеся в них каталоги?
Если это поможет, вот несколько команд для создания тестовой структуры с плохими именами и символическими ссылками:
mkdir -p cfiles/{1..3}/{a..b} && cd cfiles
mkdir space\ d
touch -- i.c -.c bad\ .c 'terrible
.c' not-c .hidden.c
for d in space\ d 1 2 2/{a..b} 3/b; do cp -t "$d" -- *.c; done
ln -s 2 dirlink
ln -s 3/b/i.c filelink.c
В полученной структуре 7 каталогов содержат .c
файлы, а 29 обычных файлов заканчиваются .c
(если dotglob
во время выполнения команд отключено) (если я пропустил, пожалуйста, дайте мне знать). Это цифры, которые я хочу.
Пожалуйста, не стесняйтесь не использовать этот конкретный тест.
Примечание: ответы в любой оболочке или на другом языке будут проверены и оценены мной. Если мне нужно установить новые пакеты, нет проблем. Если вы знаете решение с графическим интерфейсом, я призываю вас поделиться им (но я не могу пойти так далеко, чтобы установить целый DE для его тестирования) :) Я использую Ubuntu MATE 17.10.
Ответы:
Я не проверял вывод с помощью символических ссылок, но:
find
печатает имя каталога каждого.c
найденного файла.sort | uniq -c
will даст нам количество файлов в каждом каталоге (здесьsort
может быть ненужно, не уверен)sed
, я заменяю имя каталога на1
, таким образом устраняя все возможные странные символы, только с количеством и1
оставшимисяtr
d
здесь по существу так же, какNR
. Я мог бы опустить вставку1
вsed
команду и просто напечататьNR
здесь, но я думаю, что это немного яснее.До тех пор
tr
, пока данные не разделены NUL, они защищены от всех допустимых имен файлов.С zsh и bash вы можете использовать
printf %q
для получения строки в кавычках, в которой не будет символов новой строки. Таким образом, вы можете сделать что-то вроде:Однако, хотя
**
предполагается , что он не будет расширяться для символических ссылок на каталоги , я не смог получить желаемый результат на bash 4.4.18 (1) (Ubuntu 16.04).Но zsh работал нормально, и команду можно упростить:
D
позволяет этому глобусу выбирать точечные файлы,.
выбирает обычные файлы (то есть, не символические ссылки) и:h
печатает только путь к каталогу, а не имя файла (например,find
«s»%h
) (см. разделы « Генерация имени файла и модификаторы» ). Таким образом, с помощью команды awk нам просто нужно подсчитать количество появляющихся уникальных каталогов, а количество строк - это количество файлов.источник
29 7
. Если я добавлю-L
кfind
, это идет до41 10
. Какой выход вам нужен?Python имеет
os.walk
, что делает такие задачи простыми, интуитивно понятными и автоматически устойчивыми даже перед лицом странных имен файлов, таких как те, которые содержат символы новой строки. Этот сценарий Python 3, который я первоначально разместил в чате , предназначен для запуска в текущем каталоге (но он не обязательно должен находиться в текущем каталоге, и вы можете изменить путь, по которому он проходитos.walk
):Это печатает число каталогов, которые непосредственно содержат по крайней мере один файл, имя которого заканчивается
.c
, после пробела, а затем количество файлов, имена которых заканчиваются.c
. «Скрытые» файлы - то есть файлы, имена которых начинаются с.
- включены, и скрытые каталоги просматриваются аналогичным образом.os.walk
рекурсивно пересекает иерархию каталогов. Он перечисляет все каталоги, которые рекурсивно доступны из начальной точки, которую вы им даете, получая информацию о каждом из них в виде кортежа из трех значенийroot, dirs, files
. Для каждого каталога, в который он попадает (включая первый, имя которого вы даете):root
содержит путь к этому каталогу. Обратите внимание, что это совершенно не связано с «корневым каталогом» системы/
(и также не связано с ним/root
), хотя оно и пойдет на это, если вы начнете там. В этом случаеroot
начинается с пути.
--ie, текущего каталога - и идет везде под ним.dirs
содержит список путей всех подкаталогов каталога, имя которого в настоящее время хранится вroot
.files
содержит список путей всех файлов, которые находятся в каталоге, чье имя в настоящее время хранится,root
но которые сами не являются каталогами. Обратите внимание, что это включает в себя другие виды файлов, отличные от обычных файлов, в том числе символические ссылки, но похоже, что вы не ожидаете, что такие записи заканчиваются,.c
и заинтересованы в том, чтобы увидеть какие-либо записи.В этом случае мне нужно только изучить третий элемент кортежа
files
(который я называюfs
в сценарии). Как иfind
команда, Pythonos.walk
переходит в подкаталоги для меня; единственное, что я должен проверить сам - это имена файлов, которые каждый из них содержит. В отличие отfind
команды, однако,os.walk
автоматически предоставляет мне список этих имен файлов.Этот скрипт не следует по символическим ссылкам. Скорее всего, вы не хотите, чтобы символические ссылки использовались для такой операции, потому что они могут образовывать циклы, и потому что даже если циклов нет, одни и те же файлы и каталоги могут просматриваться и подсчитываться несколько раз, если они доступны через разные символические ссылки.
Если вы когда-нибудь захотели
os.walk
перейти по символическим ссылкам - что вы обычно не хотели бы - тогда вы можете перейтиfollowlinks=true
к нему. То есть вместо того, чтобы писать,os.walk('.')
ты можешь писатьos.walk('.', followlinks=true)
. Я повторяю, что вы бы редко этого хотели, особенно для такой задачи, когда вы рекурсивно перечисляете всю структуру каталогов, независимо от ее размера, и подсчитываете все файлы в ней, которые удовлетворяют некоторому требованию.источник
Найти + Perl:
объяснение
Команда
find
найдет все обычные файлы (без символических ссылок или каталогов), а затем напечатает имя каталога, в котором они находятся (%h
), а затем\0
.perl -0 -ne
: прочитайте строку за строкой (-n
) и примените скрипт, заданный-e
для каждой строки.-0
Устанавливает входную строку разделитель для\0
так что мы можем прочитать нуль-разделители входа.$k{$_}++
:$_
это специальная переменная, которая принимает значение текущей строки. Это используется как ключ к хешу%k
, значения которого - количество раз, которое каждая строка ввода (имя каталога) была замечена.}{
: это сокращенный способ написанияEND{}
. Любые команды после}{
будут выполнены один раз, после того, как все входные данные были обработаны.print scalar keys %k, " $.\n"
:keys %k
возвращает массив ключей в хэше%k
.scalar keys %k
дает количество элементов в этом массиве, количество просмотренных каталогов. Это печатается вместе с текущим значением$.
, специальной переменной, которая содержит текущий номер строки ввода. Так как это выполняется в конце, текущий номер строки ввода будет номером последней строки, так что количество строк, видимых до сих пор.Вы можете расширить команду perl для этого, для ясности:
источник
Вот мое предложение:
Этот короткий сценарий создает временный файл, находит все файлы в текущем каталоге и под ним, оканчивающиеся на,
.c
и записывает список в временный файл .grep
затем используется для подсчета файлов (следуя инструкциям Как получить количество файлов в каталоге с помощью командной строки? ) дважды: во второй раз каталоги, которые перечислены несколько раз, удаляются с использованиемsort -u
после удаления имен файлов из каждой строки с помощьюsed
.Это также правильно работает с символами новой строки в именах файлов:
grep -c /
считает только строки с косой чертой и, следовательно, рассматривает только первую строку многострочного имени файла в списке.Выход
источник
Небольшой шеллскрипт
Я предлагаю небольшой командный скрипт bash с двумя основными командными строками (и переменную,
filetype
чтобы облегчить переключение для поиска других типов файлов).Он не ищет или в символических ссылках, только обычные файлы.
Подробный шеллскрипт
Это более подробная версия, которая также рассматривает символические ссылки,
Тестовый вывод
Из краткого сценария:
Из подробного шеллскрипта:
источник
Простой Perl один лайнер:
Или проще с
find
командой:Если вы любите играть в гольф и имеете недавно (например, менее десяти лет) Perl:
источник
Попробуйте использовать
locate
команду, которая намного быстрее, чемfind
команда.Бег на тестовых данных
Спасибо Муру за его ответ, который помог мне убрать символические ссылки из числа файлов в ответах Unix и Linux .
Спасибо Тердону за его ответ
$PWD
(не направленный на меня) в ответе Unix & Linux .Оригинальный ответ ниже, на который ссылаются комментарии
Короткая форма:
sudo updatedb
Обновите базу данных, используемуюlocate
командой, если.c
файлы были созданы сегодня или если вы удалили.c
файлы сегодня.locate -cr "$PWD.*\.c$"
найдите все.c
файлы в текущем каталоге и его дочерние элементы ($PWD
). Вместо того, чтобы печатать имена файлов и печатать количество с-c
аргументом. Вr
определяет регулярное выражение , а не по умолчанию*pattern*
соответствия , которые могут дать слишком много результатов.locate -r "$PWD.*\.c$" | sed 's%/[^/]*$%/%' | uniq -c | wc -l
, Найдите все*.c
файлы в текущем каталоге и ниже. Удалите имя файла,sed
оставив только имя каталога. Подсчитайте количество файлов в каждом каталоге, используяuniq -c
. Подсчитать количество каталогов сwc -l
.Начать в текущем каталоге с одной строки
Обратите внимание, как количество файлов и количество каталогов изменились. Я полагаю, что у всех пользователей есть
/usr/src
каталог, и они могут выполнять вышеуказанные команды с разным количеством в зависимости от количества установленных ядер.Длинная форма:
Длинная форма включает в себя время, чтобы вы могли видеть, насколько быстрее
locate
закончитсяfind
. Даже если вам нужно бежать,sudo updatedb
он во много раз быстрее, чем одинfind /
.Примечание. Это все файлы на ВСЕХ дисках и разделах. то есть мы можем искать команды Windows тоже:
У меня есть три раздела NTFS Windows 10, автоматически смонтированные в
/etc/fstab
. Будьте в курсе, найти все знает!Интересный граф:
Для подсчета 1 637 135 файлов в 286 705 каталогах требуется 15 секунд. YMMV.
Для подробного
locate
разбора обработки регулярных выражений команды (кажется, не требуется в этом вопросе и ответе, но используется на всякий случай), пожалуйста, прочитайте это: Использовать «locate» в каком-то определенном каталоге?Дополнительное чтение из последних статей:
источник
.c
(обратите внимание, что он сломается, если-.c
в текущем каталоге есть файл с именем, поскольку вы не цитируете*.c
), а затем напечатает все каталоги. в системе, независимо от того, содержат ли они .c файлы.~/my_c_progs/*.c
. Он насчитывает 638 каталогов с.c
программами, общее количество каталогов будет показано позже286,705
. Я исправлю ответ в двойной кавычке `" * .c ". Спасибо за чаевые.locate -r "/path/to/dir/.*\.c$"
, но это нигде не упоминается в вашем ответе. Вы только даете ссылку на другой ответ, который упоминает об этом, но без объяснения того, как адаптировать его для ответа на вопрос, задаваемый здесь. Весь ваш ответ сфокусирован на том, как подсчитать общее количество файлов и каталогов в системе, что не относится к заданному вопросу: «Как я могу подсчитать количество файлов .c и количество содержащихся каталогов. c файлами в определенном каталоге ". Кроме того, ваши цифры неверны, попробуйте это на примере в ОП.$PWD
переменной: unix.stackexchange.com/a/188191/200094$PWD
нет символов, которые могут быть особенными в регулярном выражении