Как я могу подсчитать файлы с определенным расширением и каталогами, в которых они находятся?

Экстренное выключение изящным способом

Все время нажмите клавиши alt и PrintScreen и одну из клавиш R E I S U O один за другим. Если это ноутбук, вам также может понадобиться нажать клавишу Fn следующим образом: Нажмите три клавиши Fn и alt и клавиши PrintScreen все время и одну из клавиш R E I S U O один за другим медленно.

Аварийная перезагрузка изящным способом

Соответствующая последовательность системных запросов для перезагрузки аналогична.

O 'Off' заменяется на B 'Boot'.

Нажимайте одновременно клавиши alt и PrintScreen и одну из клавиш REISUO один за другим.

Нажимайте одновременно клавиши alt и PrintScreen и одну из клавиш REISUO медленно.

Если это ноутбук, вам также может потребоваться нажать клавишу Fn, например так: Нажмите три клавиши Fn и alt и клавиши PrintScreen все время и одну из клавиш REISUO один за другим медленно.

на ноутбуке, возможно, три клавиши Fn и alt и PrintScreen все время

Для получения более подробной информации см. следующие ссылки,

Перезапустите Ubuntu с клавиатуры

14
задан 9 April 2018 в 13:21

21 ответ

Я не изучал вывод с символическими ссылками, но:

find . -type f -iname '*.c' -printf '%h\0' |
  sort -z |
  uniq -zc |
  sed -zr 's/([0-9]) .*/\1 1/' |
  tr '\0' '\n' |
  awk '{f += $1; d += $2} END {print f, d}'
Команда find печатает имя каталога каждого найденного файла .c. sort | uniq -c предоставит нам, сколько файлов находится в каждом каталоге (здесь sort может быть ненужным, а не обязательно) с помощью sed, я заменяю имя каталога на 1, тем самым устраняя все возможные странные символы, счетчик и 1 останутся, позволяя мне преобразовать вывод, выделенный в новую строку, с помощью tr, который затем я суммирую с awk, чтобы получить общее количество файлов и количество каталогов, содержащих эти файлы. Обратите внимание, что d здесь по существу совпадает с NR. Я мог бы пропустить вставку 1 в команду sed и просто напечатать NR здесь, но я думаю, что это немного яснее.

До tr данные делятся на NUL, безопасны для всех допустимых имен файлов.

С помощью zsh и bash вы можете использовать [ f19], чтобы получить строку с кавычками, в которой не было бы новых строк. Таким образом, вы можете сделать что-то вроде:

shopt -s globstar dotglob nocaseglob
printf "%q\n" **/*.c | awk -F/ '{NF--; f++} !c[$0]++{d++} END {print f, d}'

Однако, хотя ** не предполагается расширять для символических ссылок на каталоги, я не смог получить желаемый результат в bash 4.4.18 (1) (Ubuntu 16.04).

$ shopt -s globstar dotglob nocaseglob
$ printf "%q\n" ./**/*.c | awk -F/ '{NF--; f++} !c[$0]++{d++} END {print f, d}'
34 15
$ echo $BASH_VERSION
4.4.18(1)-release

Но zsh работал нормально, и команда может быть упрощена:

$ printf "%q\n" ./**/*.c(D.:h) | awk '!c[$0]++ {d++} END {print NR, d}'
29 7

D позволяет этому глобусу выбирать точечные файлы , . выбирает обычные файлы (так, а не символические ссылки), а :h печатает только путь к каталогу, а не имя файла (например, find %h) (см. разделы ** не предполагается расширить для символических ссылок на каталоги и модификаторы). Таким образом, с помощью команды awk нам просто нужно подсчитать количество уникальных каталогов, а количество строк - количество файлов.

16
ответ дан 22 May 2018 в 11:32
  • 1
    Это потрясающе. Использует именно то, что нужно и не больше. Спасибо за преподавание :) – Zanna 9 April 2018 в 11:55
  • 2
    @Zanna, если вы публикуете некоторые команды для воссоздания структуры каталогов с символическими ссылками и ожидаемого вывода с символическими ссылками, я мог бы исправить это соответствующим образом. – muru 9 April 2018 в 11:57
  • 3
    Я добавил несколько команд, чтобы создать (ненужно сложную, как обычно) тестовую структуру с символическими ссылками. – Zanna 9 April 2018 в 12:58
  • 4
    @Zanna Я думаю, что эта команда не нуждается в настройках, чтобы получить 29 7. Если я добавлю -L в find, это будет до 41 10. Какой результат вам нужен? – muru 9 April 2018 в 13:09
  • 5
    Добавлен метод zsh + awk. Вероятно, есть какой-то способ заставить zsh самостоятельно распечатать счет для меня, но не знаю, как это сделать. – muru 10 April 2018 в 07:38

Я не изучал вывод с символическими ссылками, но:

find . -type f -iname '*.c' -printf '%h\0' | sort -z | uniq -zc | sed -zr 's/([0-9]) .*/\1 1/' | tr '\0' '\n' | awk '{f += $1; d += $2} END {print f, d}' Команда find печатает имя каталога каждого найденного файла .c. sort | uniq -c предоставит нам, сколько файлов находится в каждом каталоге (здесь sort может быть ненужным, а не обязательно) с помощью sed, я заменяю имя каталога на 1, тем самым устраняя все возможные странные символы, счетчик и 1 останутся, позволяя мне преобразовать вывод, выделенный в новую строку, с помощью tr, который затем я суммирую с awk, чтобы получить общее количество файлов и количество каталогов, содержащих эти файлы. Обратите внимание, что d здесь по существу совпадает с NR. Я мог бы пропустить вставку 1 в команду sed и просто напечатать NR здесь, но я думаю, что это немного яснее.

До tr данные делятся на NUL, безопасны для всех допустимых имен файлов.

С помощью zsh и bash вы можете использовать printf %q, чтобы получить строку с кавычками, в которой не было бы новых строк. Таким образом, вы можете сделать что-то вроде:

shopt -s globstar dotglob nocaseglob printf "%q\n" **/*.c | awk -F/ '{NF--; f++} !c[$0]++{d++} END {print f, d}'

Однако, хотя ** не предполагается расширять для символических ссылок на каталоги, я не смог получить желаемый результат в bash 4.4.18 (1) (Ubuntu 16.04).

$ shopt -s globstar dotglob nocaseglob $ printf "%q\n" ./**/*.c | awk -F/ '{NF--; f++} !c[$0]++{d++} END {print f, d}' 34 15 $ echo $BASH_VERSION 4.4.18(1)-release

Но zsh работал нормально, и команда может быть упрощена:

$ printf "%q\n" ./**/*.c(D.:h) | awk '!c[$0]++ {d++} END {print NR, d}' 29 7

D позволяет этому глобусу выбирать точечные файлы , . выбирает обычные файлы (так, а не символические ссылки), а :h печатает только путь к каталогу, а не имя файла (например, find %h) (см. разделы ** не предполагается расширить для символических ссылок на каталоги и модификаторы). Таким образом, с помощью команды awk нам просто нужно подсчитать количество уникальных каталогов, а количество строк - количество файлов.

16
ответ дан 17 July 2018 в 17:12

Я не изучал вывод с символическими ссылками, но:

find . -type f -iname '*.c' -printf '%h\0' | sort -z | uniq -zc | sed -zr 's/([0-9]) .*/\1 1/' | tr '\0' '\n' | awk '{f += $1; d += $2} END {print f, d}' Команда find печатает имя каталога каждого найденного файла .c. sort | uniq -c предоставит нам, сколько файлов находится в каждом каталоге (здесь sort может быть ненужным, а не обязательно) с помощью sed, я заменяю имя каталога на 1, тем самым устраняя все возможные странные символы, счетчик и 1 останутся, позволяя мне преобразовать вывод, выделенный в новую строку, с помощью tr, который затем я суммирую с awk, чтобы получить общее количество файлов и количество каталогов, содержащих эти файлы. Обратите внимание, что d здесь по существу совпадает с NR. Я мог бы пропустить вставку 1 в команду sed и просто напечатать NR здесь, но я думаю, что это немного яснее.

До tr данные делятся на NUL, безопасны для всех допустимых имен файлов.

С помощью zsh и bash вы можете использовать printf %q, чтобы получить строку с кавычками, в которой не было бы новых строк. Таким образом, вы можете сделать что-то вроде:

shopt -s globstar dotglob nocaseglob printf "%q\n" **/*.c | awk -F/ '{NF--; f++} !c[$0]++{d++} END {print f, d}'

Однако, хотя ** не предполагается расширять для символических ссылок на каталоги, я не смог получить желаемый результат в bash 4.4.18 (1) (Ubuntu 16.04).

$ shopt -s globstar dotglob nocaseglob $ printf "%q\n" ./**/*.c | awk -F/ '{NF--; f++} !c[$0]++{d++} END {print f, d}' 34 15 $ echo $BASH_VERSION 4.4.18(1)-release

Но zsh работал нормально, и команда может быть упрощена:

$ printf "%q\n" ./**/*.c(D.:h) | awk '!c[$0]++ {d++} END {print NR, d}' 29 7

D позволяет этому глобусу выбирать точечные файлы , . выбирает обычные файлы (так, а не символические ссылки), а :h печатает только путь к каталогу, а не имя файла (например, find %h) (см. разделы ** не предполагается расширить для символических ссылок на каталоги и модификаторы). Таким образом, с помощью команды awk нам просто нужно подсчитать количество уникальных каталогов, а количество строк - количество файлов.

16
ответ дан 23 July 2018 в 18:03

Python имеет os.walk, что делает такие задачи такими же легкими, интуитивно понятными и автоматически надежными даже перед лицом странных имен файлов, таких как те, которые содержат символы новой строки. Этот сценарий Python 3, который я изначально разместил в чате, предназначен для запуска в текущем каталоге (но он не должен находиться в текущем каталоге, и вы можете изменить его путь к os.walk), :

#!/usr/bin/env python3

import os

dc = fc = 0
for _, _, fs in os.walk('.'):
    c = sum(f.endswith('.c') for f in fs)
    if c:
        dc += 1
        fc += c
print(dc, fc)

Это печатает количество каталогов, в которых содержится хотя бы один файл, имя которого заканчивается на .c, за которым следует пробел, за которым следует количество файлов, имена которых заканчиваются на .c , «Скрытые» файлы, то есть файлы, имена которых начинаются с ., включены, а скрытые каталоги аналогичным образом пересекаются.

os.walk Он перечисляет все каталоги, которые рекурсивно доступны из исходной точки, которую вы им даете, приводя информацию о каждом из них как кортеж из трех значений root, dirs, files. Для каждого каталога он переходит к (включая первый, имя которого вы даете ему):

root содержит путь к этому каталогу. Обратите внимание, что это абсолютно не связано с «корневым каталогом» системы / (а также не связано с /root), хотя оно будет идти к тем, если вы начнете там. В этом случае root начинается с пути . - i.e., Текущего каталога - и проходит повсюду под ним. dirs содержит список путей всех подкаталогов каталога, имя которого в настоящее время хранится в root. files содержит список путей всех файлов, которые находятся в каталоге, имя которого в настоящее время хранится в root, но которые не являются самими каталогами. Обратите внимание, что это включает в себя другие типы файлов, чем обычные файлы, включая символические ссылки, но похоже, что вы не ожидаете окончания таких записей в .c и заинтересованы в том, чтобы видеть что-либо подобное.

В этом случае мне нужно только изучить третий элемент кортежа, files (который я называю fs в скрипте). Как и команда find, Python os.walk перемещается в подкаталоги для меня; единственное, что я должен проверить сам, это имена файлов, в которые каждый из них содержится. В отличие от команды find, os.walk автоматически предоставляет мне список этих имен файлов.

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

Если вы когда-либо хотели os.walk следовать символическим ссылкам, что вы обычно не хотели, то вы можете передать followlinks=true на него. То есть вместо записи os.walk('.') вы можете написать os.walk('.', followlinks=true). Я повторяю, что вы редко этого хотели, особенно для такой задачи, в которой вы рекурсивно перечисляете всю структуру каталогов, независимо от того, насколько она велика, и подсчитывая все файлы в ней, которые удовлетворяют некоторым требованиям.

11
ответ дан 22 May 2018 в 11:32

Найти + Perl:

$ find . -type f -iname '*.c' -printf '%h\0' | 
    perl -0 -ne '$k{$_}++; }{ print scalar keys %k, " $.\n" '
7 29

Объяснение

Команда find найдет любые обычные файлы (так что никаких символических ссылок или каталогов), а затем напечатать имя каталога, которое они находятся в (%h), а затем \0.

perl -0 -ne: прочитайте ввод строки за строкой (-n) и примените скрипт, заданный -e для каждой строки. [F9] устанавливает разделитель входных строк на \0, чтобы мы могли считывать ввод с нулевым ограничением. $k{$_}++: $_ - специальная переменная, которая принимает значение текущей строки. Это используется как ключ к хешу %k, значения которого представляют собой количество раз, когда была видна каждая строка ввода (имя каталога). }{: это сокращенный способ написания END{}. Любые команды после }{ будут выполняться один раз, после того, как все данные будут обработаны. print scalar keys %k, " $.\n": keys %k возвращает массив ключей в хеше %k. scalar keys %k дает количество элементов в этом массиве, количество просмотренных каталогов. Это печатается вместе с текущим значением $., специальной переменной, которая содержит текущий номер строки ввода. Так как это выполняется в конце, текущий номер строки ввода будет номером последней строки, поэтому количество строк, видимых до сих пор.

Вы можете расширить команду perl для этого, для ясности:

find  . -type f -iname '*.c' -printf '%h\0' | 
    perl -0 -e 'while($line = <STDIN>){
                    $dirs{$line}++; 
                    $tot++;
                } 
                $count = scalar keys %dirs; 
                print "$count $tot\n" '
7
ответ дан 22 May 2018 в 11:32

Вот мое предложение:

#!/bin/bash
tempfile=$(mktemp)
find -type f -name "*.c" -prune >$tempfile
grep -c / $tempfile
sed 's_[^/]*$__' $tempfile | sort -u | grep -c /

Этот короткий скрипт создает временный файл, находит каждый файл в и под текущим каталогом, заканчивающимся на .c, и записывает список в файл temp. grep затем используется для подсчета файлов (следуя Как я могу получить количество файлов в каталоге с помощью командной строки?) дважды: во второй раз каталоги, которые перечислены несколько раз, удаляются с помощью sort -u после удаления файлов из каждой строки, используя sed.

Это также корректно работает с новыми строками в именах файлов: grep -c / подсчитывает только строки с косой чертой и поэтому учитывает только первую строку многострочного имени файла в списке.

Выход

$ tree
.
├── 1
│   ├── 1
│   │   ├── test2.c
│   │   └── test.c
│   └── 2
│       └── test.c
└── 2
    ├── 1
    │   └── test.c
    └── 2

$ tempfile=$(mktemp);find -type f -name "*.c" -prune >$tempfile;grep -c / $tempfile;sed 's_[^/]*$__' $tempfile | sort -u | grep -c /
4
3
4
ответ дан 22 May 2018 в 11:32

Маленький shellscript

Я предлагаю небольшой командный столбец bash с двумя основными командами (и переменной filetype, чтобы было легко переключиться, чтобы искать другие типы файлов).

Он не ищет или в символических ссылках, только обычные файлы.

#!/bin/bash

filetype=c
#filetype=pdf

# count the 'filetype' files

find -type f -name "*.$filetype" -ls|sed 's#.* \./##'|wc -l | tr '\n' ' '

# count directories containing 'filetype' files

find -type d -exec bash -c "ls -AF '{}'|grep -e '\.'${filetype}$ -e '\.'${filetype}'\*'$ > /dev/null && echo '{} contains file(s)'" \;|grep 'contains file(s)$'|wc -l

Подробный shellscript

Это более подробная версия, которая также рассматривает символические ссылки, [!d4 ]

#!/bin/bash

filetype=c
#filetype=pdf

# counting the 'filetype' files

echo -n "number of $filetype files in the current directory tree: "
find -type f -name "*.$filetype" -ls|sed 's#.* \./##'|wc -l

echo -n "number of $filetype symbolic links in the current directory tree: "
find -type l -name "*.$filetype" -ls|sed 's#.* \./##'|wc -l
echo -n "number of $filetype normal files in the current directory tree: "
find -type f -name "*.$filetype" -ls|sed 's#.* \./##'|wc -l
echo -n "number of $filetype symbolic links in the current directory tree including linked directories: "
find -L -type f -name "*.$filetype" -ls 2> /tmp/c-counter |sed 's#.* \./##' | wc -l; cat /tmp/c-counter; rm /tmp/c-counter

# list directories with and without 'filetype' files (good for manual checking; comment away after test)
echo '---------- list directories:'
 find    -type d -exec bash -c "ls -AF '{}'|grep -e '\.'${filetype}$ -e '\.'${filetype}'\*'$ > /dev/null && echo '{} contains file(s)' || echo '{} empty'" \;
echo ''
#find -L -type d -exec bash -c "ls -AF '{}'|grep -e '\.'${filetype}$ -e '\.'${filetype}'\*'$ > /dev/null && echo '{} contains file(s)' || echo '{} empty'" \;

# count directories containing 'filetype' files

echo -n "number of directories with $filetype files: "
find -type d -exec bash -c "ls -AF '{}'|grep -e '\.'${filetype}$ -e '\.'${filetype}'\*'$ > /dev/null && echo '{} contains file(s)'" \;|grep 'contains file(s)$'|wc -l

# list and count directories including symbolic links, containing 'filetype' files
echo '---------- list all directories including symbolic links:'
find -L -type d -exec bash -c "ls -AF '{}' |grep -e '\.'${filetype}$ -e '\.'${filetype}'\*'$ > /dev/null && echo '{} contains file(s)' || echo '{} empty'" \;
echo ''
echo -n "number of directories (including symbolic links) with $filetype files: "
find -L -type d -exec bash -c "ls -AF '{}'|grep -e '\.'${filetype}$ -e '\.'${filetype}'\*'$ > /dev/null && echo '{} contains file(s)'" \; 2>/dev/null |grep 'contains file(s)$'|wc -l

# count directories without 'filetype' files (good for checking; comment away after test)

echo -n "number of directories without $filetype files: "
find -type d -exec bash -c "ls -AF '{}'|grep -e '\.'${filetype}$ -e '\.'${filetype}'\*'$ > /dev/null || echo '{} empty'" \;|grep 'empty$'|wc -l

Тестовый выход

Из короткого shellscript:

$ ./ccntr 
29 7

Из подробного shellscript:

$ LANG=C ./c-counter
number of c files in the current directory tree: 29
number of c symbolic links in the current directory tree: 1
number of c normal files in the current directory tree: 29
number of c symbolic links in the current directory tree including linked directories: 42
find: './cfiles/2/2': Too many levels of symbolic links
find: './cfiles/dirlink/2': Too many levels of symbolic links
---------- list directories:
. empty
./cfiles contains file(s)
./cfiles/2 contains file(s)
./cfiles/2/b contains file(s)
./cfiles/2/a contains file(s)
./cfiles/3 empty
./cfiles/3/b contains file(s)
./cfiles/3/a empty
./cfiles/1 contains file(s)
./cfiles/1/b empty
./cfiles/1/a empty
./cfiles/space d contains file(s)

number of directories with c files: 7
---------- list all directories including symbolic links:
. empty
./cfiles contains file(s)
./cfiles/2 contains file(s)
find: './cfiles/2/2': Too many levels of symbolic links
./cfiles/2/b contains file(s)
./cfiles/2/a contains file(s)
./cfiles/3 empty
./cfiles/3/b contains file(s)
./cfiles/3/a empty
./cfiles/dirlink empty
find: './cfiles/dirlink/2': Too many levels of symbolic links
./cfiles/dirlink/b contains file(s)
./cfiles/dirlink/a contains file(s)
./cfiles/1 contains file(s)
./cfiles/1/b empty
./cfiles/1/a empty
./cfiles/space d contains file(s)

number of directories (including symbolic links) with c files: 9
number of directories without c files: 5
$ 
4
ответ дан 22 May 2018 в 11:32

Простой Perl один лайнер:

perl -MFile::Find=find -le'find(sub{/\.c\z/ and -f and $c{$File::Find::dir}=++$c}, @ARGV); print 0 + keys %c, " $c"' dir1 dir2

Или проще с командой find:

find dir1 dir2 -type f -name '*.c' -printf '%h\0' | perl -l -0ne'$c{$_}=1}{print 0 + keys %c, " $."'

Если вам нравится играть в гольф и иметь последние (например, менее десятилетия) Perl:

perl -MFile::Find=find -E'find(sub{/\.c$/&&-f&&($c{$File::Find::dir}=++$c)},".");say 0+keys%c," $c"'
find -type f -name '*.c' -printf '%h\0'|perl -0nE'$c{$_}=1}{say 0+keys%c," $."'
4
ответ дан 22 May 2018 в 11:32

Используйте команду locate, которая намного быстрее, чем команда find.

Выполнение тестовых данных

$ sudo updatedb # necessary if files in focus were added `cron` daily.
$ printf "Number Files: " && locate -0r "$PWD.*\.c$" | xargs -0 -I{} sh -c 'test ! -L "$1" && echo "regular file"' _  {} | wc -l &&  printf "Number Dirs.: " && locate -r "$PWD.*\.c$" | sed 's%/[^/]*$%/%' | uniq -cu | wc -l
Number Files: 29
Number Dirs.: 7

Спасибо Муру за его ответ, чтобы помочь мне путем удаления символических ссылок из количества файлов в Unix & amp; Ответ на Linux.

Спасибо Тердону за его ответ $PWD (не направленный на меня) в Unix & amp; Ответ на Linux .

Оригинальный ответ ниже, на который ссылаются комментарии

Короткая форма:

$ cd /
$ sudo updatedb
$ printf "Number Files: " && locate -cr "$PWD.*\.c$"
Number Files: 3523
$ printf "Number Dirs.: " && locate -r "$PWD.*\.c$" | sed 's%/[^/]*$%/%' | uniq -c | wc -l 
Number Dirs.: 648
sudo updatedb Обновить базу данных, используемую командой locate, если сегодня были созданы файлы .c или если вы удалили файлы .c сегодня. locate -cr "$PWD.*\.c$" найдите все .c файлы в текущем каталоге и его дочерние элементы ($PWD). Вместо того, чтобы печатать имена файлов и распечатывать счетчик с аргументом -c. Параметр r задает регулярное выражение вместо соответствия по умолчанию *pattern*, которое может дать слишком много результатов. [F20]. Найдите все *.c файлы в текущем каталоге и ниже. Удалите имя файла с sed, оставив только имя каталога. Подсчитайте количество файлов в каждом каталоге с помощью uniq -c. Подсчитайте количество каталогов с помощью wc -l.

Начать с текущего каталога с помощью одного слоя

$ cd /usr/src
$ printf "Number Files: " && locate -cr "$PWD.*\.c$" &&  printf "Number Dirs.: " && locate -r "$PWD.*\.c$" | sed 's%/[^/]*$%/%' | uniq -c | wc -l
Number Files: 3430
Number Dirs.: 624

Обратите внимание, что количество файлов и количество каталогов изменились. Я полагаю, что все пользователи имеют каталог /usr/src и могут работать над командами с разными значениями в зависимости от количества установленных ядер.

Длинная форма:

Длинная форма включает время, чтобы вы может видеть, насколько быстрее locate закончилось find. Даже если вам нужно запустить sudo updatedb, он во много раз быстрее, чем один find /.

───────────────────────────────────────────────────────────────────────────────────────────
rick@alien:~/Downloads$ sudo time updatedb
0.58user 1.32system 0:03.94elapsed 48%CPU (0avgtext+0avgdata 7568maxresident)k
48inputs+131920outputs (1major+3562minor)pagefaults 0swaps
───────────────────────────────────────────────────────────────────────────────────────────
rick@alien:~/Downloads$ time (printf "Number Files: " && locate -cr $PWD".*\.c$")
Number Files: 3523

real    0m0.775s
user    0m0.766s
sys     0m0.012s
───────────────────────────────────────────────────────────────────────────────────────────
rick@alien:~/Downloads$ time (printf "Number Dirs.: " && locate -r $PWD".*\.c$" | sed 's%/[^/]*$%/%' | uniq -c | wc -l) 
Number Dirs.: 648

real    0m0.778s
user    0m0.788s
sys     0m0.027s
───────────────────────────────────────────────────────────────────────────────────────────

Примечание. Это все файлы на ВСЕ приводах и разделах. т.е. мы можем также искать команды Windows:

$ time (printf "Number Files: " && locate *.exe -c)
Number Files: 6541

real    0m0.946s
user    0m0.761s
sys     0m0.060s
───────────────────────────────────────────────────────────────────────────────────────────
rick@alien:~/Downloads$ time (printf "Number Dirs.: " && locate *.exe | sed 's%/[^/]*$%/%' | uniq -c | wc -l) 
Number Dirs.: 3394

real    0m0.942s
user    0m0.803s
sys     0m0.092s

У меня есть три раздела Windows NT NTFS, автоматически смонтированные в /etc/fstab.

Интересный подсчет:

$ time (printf "Number Files: " && locate / -c &&  printf "Number Dirs.: " && locate / | sed 's%/[^/]*$%/%' | uniq -c | wc -l)
Number Files: 1637135
Number Dirs.: 286705

real    0m15.460s
user    0m13.471s
sys     0m2.786s

Требуется 15 секунд, чтобы посчитать 1637135 файлов в 286 705 каталогах. YMMV.

Для детальной разбивки на обработку регулярных выражений команды locate (кажется, не требуется в этом Q & amp; A, но используется на всякий случай), пожалуйста, прочтите следующее: Используйте " locate " в каком-то конкретном каталоге?

Дополнительная информация из последних статей:

sudo updatedb Обновить базу данных, используемую командой locate, если файлы .c были созданы сегодня или если вы HowtoForge - Linux Найдите команду для начинающих (8 примеров) locate -cr "$PWD.*\.c$" найдите все .c файлы в текущем каталоге и его дочерние элементы ($PWD). Вместо того, чтобы печатать имена файлов и распечатывать счетчик с аргументом -c. Параметр r задает регулярное выражение вместо соответствия по умолчанию *pattern*, которое может дать слишком много результатов.
2
ответ дан 22 May 2018 в 11:32
  • 1
    Это не учитывает файлы в определенном каталоге. Как вы отмечаете, он подсчитывает все файлы (или каталогов, или любой другой тип файла) соответствие [F1] (заметим, что она сломается, если есть файл с именем -.c в текущем каталоге, так как вы не процитировать ), а затем распечатает все каталоги в системе, независимо от того, содержат ли они файлы .c. – terdon♦ 10 April 2018 в 12:01
  • 2
    @terdon Вы можете передать каталог ~/my_c_progs/*.c. Он подсчитывает 638 каталогов с программами .c, общие каталоги отображаются позже как 286,705. Я переработаю ответ на двойную цитату `" *. C & quot ;. Спасибо за совет. – WinEunuuchs2Unix 10 April 2018 в 13:03
  • 3
    Да, вы можете использовать что-то вроде locate -r "/path/to/dir/.*\.c$", но это не упоминается нигде в вашем ответе. Вы даете ссылку на другой ответ, который упоминает об этом, но без объяснения того, как его адаптировать, чтобы ответить на вопрос, задаваемый здесь. Весь ваш ответ сосредоточен на том, как подсчитать общее количество файлов и каталогов в системе, что не имеет отношения к заданному вопросу, который был «как я могу подсчитать количество файлов .c и количество каталогов, содержащих .c файлы в определенном каталоге ". Кроме того, ваши цифры неверны, попробуйте его на примере в OP. – terdon♦ 10 April 2018 в 13:45
  • 4
    @terdon Спасибо за ваш вклад. Я улучшил ответ с вашими предложениями и ответом, который вы разместили на другом сайте SE для переменной $PWD: unix.stackexchange.com/a/188191/200094 – WinEunuuchs2Unix 11 April 2018 в 03:45
  • 5
    Теперь вы должны убедиться, что $PWD не содержит символов, которые могут быть специальными в регулярном выражении – muru 11 April 2018 в 03:55

Вот мое предложение:

#!/bin/bash tempfile=$(mktemp) find -type f -name "*.c" -prune >$tempfile grep -c / $tempfile sed 's_[^/]*$__' $tempfile | sort -u | grep -c /

Этот короткий скрипт создает временный файл, находит каждый файл в и под текущим каталогом, заканчивающимся на .c, и записывает список в файл temp. grep затем используется для подсчета файлов (следуя Как я могу получить количество файлов в каталоге с помощью командной строки?) дважды: во второй раз каталоги, которые перечислены несколько раз, удаляются с помощью sort -u после удаления файлов из каждой строки, используя sed.

Это также корректно работает с новыми строками в именах файлов: grep -c / подсчитывает только строки с косой чертой и поэтому учитывает только первую строку многострочного имени файла в списке.

Выход

$ tree . ├── 1 │   ├── 1 │   │   ├── test2.c │   │   └── test.c │   └── 2 │   └── test.c └── 2    ├── 1    │   └── test.c    └── 2 $ tempfile=$(mktemp);find -type f -name "*.c" -prune >$tempfile;grep -c / $tempfile;sed 's_[^/]*$__' $tempfile | sort -u | grep -c / 4 3
4
ответ дан 17 July 2018 в 17:12

Python имеет os.walk, что делает такие задачи такими же легкими, интуитивно понятными и автоматически надежными даже перед лицом странных имен файлов, таких как те, которые содержат символы новой строки. Этот сценарий Python 3, который я изначально разместил в чате, предназначен для запуска в текущем каталоге (но он не должен находиться в текущем каталоге, и вы можете изменить его путь к os.walk), :

#!/usr/bin/env python3 import os dc = fc = 0 for _, _, fs in os.walk('.'): c = sum(f.endswith('.c') for f in fs) if c: dc += 1 fc += c print(dc, fc)

Это печатает количество каталогов, в которых содержится хотя бы один файл, имя которого заканчивается на .c, за которым следует пробел, за которым следует количество файлов, имена которых заканчиваются на .c , «Скрытые» файлы, то есть файлы, имена которых начинаются с ., включены, а скрытые каталоги аналогичным образом пересекаются.

os.walk Он перечисляет все каталоги, которые рекурсивно доступны из исходной точки, которую вы им даете, приводя информацию о каждом из них как кортеж из трех значений root, dirs, files. Для каждого каталога он переходит к (включая первый, имя которого вы даете ему):

root содержит путь к этому каталогу. Обратите внимание, что это абсолютно не связано с «корневым каталогом» системы / (а также не связано с /root), хотя оно будет идти к тем, если вы начнете там. В этом случае root начинается с пути . - i.e., Текущего каталога - и проходит повсюду под ним. dirs содержит список путей всех подкаталогов каталога, имя которого в настоящее время хранится в root. files содержит список путей всех файлов, которые находятся в каталоге, имя которого в настоящее время хранится в root, но которые не являются самими каталогами. Обратите внимание, что это включает в себя другие типы файлов, чем обычные файлы, включая символические ссылки, но похоже, что вы не ожидаете окончания таких записей в .c и заинтересованы в том, чтобы видеть что-либо подобное.

В этом случае мне нужно только изучить третий элемент кортежа, files (который я называю fs в скрипте). Как и команда find, Python os.walk перемещается в подкаталоги для меня; единственное, что я должен проверить сам, это имена файлов, в которые каждый из них содержится. В отличие от команды find, os.walk автоматически предоставляет мне список этих имен файлов.

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

Если вы когда-либо хотели os.walk следовать символическим ссылкам, что вы обычно не хотели, то вы можете передать followlinks=true на него. То есть вместо записи os.walk('.') вы можете написать os.walk('.', followlinks=true). Я повторяю, что вы редко этого хотели, особенно для такой задачи, в которой вы рекурсивно перечисляете всю структуру каталогов, независимо от того, насколько она велика, и подсчитывая все файлы в ней, которые удовлетворяют некоторым требованиям.

11
ответ дан 17 July 2018 в 17:12

Простой Perl один лайнер:

perl -MFile::Find=find -le'find(sub{/\.c\z/ and -f and $c{$File::Find::dir}=++$c}, @ARGV); print 0 + keys %c, " $c"' dir1 dir2

Или проще с командой find:

find dir1 dir2 -type f -name '*.c' -printf '%h\0' | perl -l -0ne'$c{$_}=1}{print 0 + keys %c, " $."'

Если вам нравится играть в гольф и иметь последние (например, менее десятилетия) Perl:

perl -MFile::Find=find -E'find(sub{/\.c$/&&-f&&($c{$File::Find::dir}=++$c)},".");say 0+keys%c," $c"' find -type f -name '*.c' -printf '%h\0'|perl -0nE'$c{$_}=1}{say 0+keys%c," $."'
4
ответ дан 17 July 2018 в 17:12

Маленький shellscript

Я предлагаю небольшой командный столбец bash с двумя основными командами (и переменной filetype, чтобы было легко переключиться, чтобы искать другие типы файлов).

Он не ищет или в символических ссылках, только обычные файлы.

#!/bin/bash filetype=c #filetype=pdf # count the 'filetype' files find -type f -name "*.$filetype" -ls|sed 's#.* \./##'|wc -l | tr '\n' ' ' # count directories containing 'filetype' files find -type d -exec bash -c "ls -AF '{}'|grep -e '\.'${filetype}$ -e '\.'${filetype}'\*'$ > /dev/null && echo '{} contains file(s)'" \;|grep 'contains file(s)$'|wc -l

Подробный shellscript

Это более подробная версия, которая также рассматривает символические ссылки,

#!/bin/bash filetype=c #filetype=pdf # counting the 'filetype' files echo -n "number of $filetype files in the current directory tree: " find -type f -name "*.$filetype" -ls|sed 's#.* \./##'|wc -l echo -n "number of $filetype symbolic links in the current directory tree: " find -type l -name "*.$filetype" -ls|sed 's#.* \./##'|wc -l echo -n "number of $filetype normal files in the current directory tree: " find -type f -name "*.$filetype" -ls|sed 's#.* \./##'|wc -l echo -n "number of $filetype symbolic links in the current directory tree including linked directories: " find -L -type f -name "*.$filetype" -ls 2> /tmp/c-counter |sed 's#.* \./##' | wc -l; cat /tmp/c-counter; rm /tmp/c-counter # list directories with and without 'filetype' files (good for manual checking; comment away after test) echo '---------- list directories:' find -type d -exec bash -c "ls -AF '{}'|grep -e '\.'${filetype}$ -e '\.'${filetype}'\*'$ > /dev/null && echo '{} contains file(s)' || echo '{} empty'" \; echo '' #find -L -type d -exec bash -c "ls -AF '{}'|grep -e '\.'${filetype}$ -e '\.'${filetype}'\*'$ > /dev/null && echo '{} contains file(s)' || echo '{} empty'" \; # count directories containing 'filetype' files echo -n "number of directories with $filetype files: " find -type d -exec bash -c "ls -AF '{}'|grep -e '\.'${filetype}$ -e '\.'${filetype}'\*'$ > /dev/null && echo '{} contains file(s)'" \;|grep 'contains file(s)$'|wc -l # list and count directories including symbolic links, containing 'filetype' files echo '---------- list all directories including symbolic links:' find -L -type d -exec bash -c "ls -AF '{}' |grep -e '\.'${filetype}$ -e '\.'${filetype}'\*'$ > /dev/null && echo '{} contains file(s)' || echo '{} empty'" \; echo '' echo -n "number of directories (including symbolic links) with $filetype files: " find -L -type d -exec bash -c "ls -AF '{}'|grep -e '\.'${filetype}$ -e '\.'${filetype}'\*'$ > /dev/null && echo '{} contains file(s)'" \; 2>/dev/null |grep 'contains file(s)$'|wc -l # count directories without 'filetype' files (good for checking; comment away after test) echo -n "number of directories without $filetype files: " find -type d -exec bash -c "ls -AF '{}'|grep -e '\.'${filetype}$ -e '\.'${filetype}'\*'$ > /dev/null || echo '{} empty'" \;|grep 'empty$'|wc -l

Тестовый выход

Из короткого shellscript:

$ ./ccntr 29 7

Из подробного shellscript:

$ LANG=C ./c-counter number of c files in the current directory tree: 29 number of c symbolic links in the current directory tree: 1 number of c normal files in the current directory tree: 29 number of c symbolic links in the current directory tree including linked directories: 42 find: './cfiles/2/2': Too many levels of symbolic links find: './cfiles/dirlink/2': Too many levels of symbolic links ---------- list directories: . empty ./cfiles contains file(s) ./cfiles/2 contains file(s) ./cfiles/2/b contains file(s) ./cfiles/2/a contains file(s) ./cfiles/3 empty ./cfiles/3/b contains file(s) ./cfiles/3/a empty ./cfiles/1 contains file(s) ./cfiles/1/b empty ./cfiles/1/a empty ./cfiles/space d contains file(s) number of directories with c files: 7 ---------- list all directories including symbolic links: . empty ./cfiles contains file(s) ./cfiles/2 contains file(s) find: './cfiles/2/2': Too many levels of symbolic links ./cfiles/2/b contains file(s) ./cfiles/2/a contains file(s) ./cfiles/3 empty ./cfiles/3/b contains file(s) ./cfiles/3/a empty ./cfiles/dirlink empty find: './cfiles/dirlink/2': Too many levels of symbolic links ./cfiles/dirlink/b contains file(s) ./cfiles/dirlink/a contains file(s) ./cfiles/1 contains file(s) ./cfiles/1/b empty ./cfiles/1/a empty ./cfiles/space d contains file(s) number of directories (including symbolic links) with c files: 9 number of directories without c files: 5 $
4
ответ дан 17 July 2018 в 17:12

Найти + Perl:

$ find . -type f -iname '*.c' -printf '%h\0' | perl -0 -ne '$k{$_}++; }{ print scalar keys %k, " $.\n" ' 7 29

Объяснение

Команда find найдет любые обычные файлы (так что никаких символических ссылок или каталогов), а затем напечатать имя каталога, которое они находятся в (%h), а затем \0.

perl -0 -ne: прочитайте ввод строки за строкой (-n) и примените скрипт, заданный -e для каждой строки. [F9] устанавливает разделитель входных строк на \0, чтобы мы могли считывать ввод с нулевым ограничением. $k{$_}++: $_ - специальная переменная, которая принимает значение текущей строки. Это используется как ключ к хешу %k, значения которого представляют собой количество раз, когда была видна каждая строка ввода (имя каталога). }{: это сокращенный способ написания END{}. Любые команды после }{ будут выполняться один раз, после того, как все данные будут обработаны. print scalar keys %k, " $.\n": keys %k возвращает массив ключей в хеше %k. scalar keys %k дает количество элементов в этом массиве, количество просмотренных каталогов. Это печатается вместе с текущим значением $., специальной переменной, которая содержит текущий номер строки ввода. Так как это выполняется в конце, текущий номер строки ввода будет номером последней строки, поэтому количество строк, видимых до сих пор.

Вы можете расширить команду perl для этого, для ясности:

find . -type f -iname '*.c' -printf '%h\0' | perl -0 -e 'while($line = <STDIN>){ $dirs{$line}++; $tot++; } $count = scalar keys %dirs; print "$count $tot\n" '
7
ответ дан 17 July 2018 в 17:12

Используйте команду locate, которая намного быстрее, чем команда find.

Выполнение тестовых данных

$ sudo updatedb # necessary if files in focus were added `cron` daily. $ printf "Number Files: " && locate -0r "$PWD.*\.c$" | xargs -0 -I{} sh -c 'test ! -L "$1" && echo "regular file"' _ {} | wc -l && printf "Number Dirs.: " && locate -r "$PWD.*\.c$" | sed 's%/[^/]*$%/%' | uniq -cu | wc -l Number Files: 29 Number Dirs.: 7

Спасибо Муру за его ответ, чтобы помочь мне путем удаления символических ссылок из количества файлов в Unix & amp; Ответ на Linux.

Спасибо Тердону за его ответ $PWD (не направленный на меня) в Unix & amp; Ответ на Linux .

Оригинальный ответ ниже, на который ссылаются комментарии

Короткая форма:

$ cd / $ sudo updatedb $ printf "Number Files: " && locate -cr "$PWD.*\.c$" Number Files: 3523 $ printf "Number Dirs.: " && locate -r "$PWD.*\.c$" | sed 's%/[^/]*$%/%' | uniq -c | wc -l Number Dirs.: 648 sudo updatedb Обновить базу данных, используемую командой locate, если сегодня были созданы файлы .c или если вы удалили файлы .c сегодня. locate -cr "$PWD.*\.c$" найдите все .c файлы в текущем каталоге и его дочерние элементы ($PWD). Вместо того, чтобы печатать имена файлов и распечатывать счетчик с аргументом -c. Параметр r задает регулярное выражение вместо соответствия по умолчанию *pattern*, которое может дать слишком много результатов. [F20]. Найдите все *.c файлы в текущем каталоге и ниже. Удалите имя файла с sed, оставив только имя каталога. Подсчитайте количество файлов в каждом каталоге с помощью uniq -c. Подсчитайте количество каталогов с помощью wc -l.

Начать с текущего каталога с помощью одного слоя

$ cd /usr/src $ printf "Number Files: " && locate -cr "$PWD.*\.c$" && printf "Number Dirs.: " && locate -r "$PWD.*\.c$" | sed 's%/[^/]*$%/%' | uniq -c | wc -l Number Files: 3430 Number Dirs.: 624

Обратите внимание, что количество файлов и количество каталогов изменились. Я полагаю, что все пользователи имеют каталог /usr/src и могут работать над командами с разными значениями в зависимости от количества установленных ядер.

Длинная форма:

Длинная форма включает время, чтобы вы может видеть, насколько быстрее locate закончилось find. Даже если вам нужно запустить sudo updatedb, он во много раз быстрее, чем один find /.

─────────────────────────────────────────────────────────────────────────────────────────── rick@alien:~/Downloads$ sudo time updatedb 0.58user 1.32system 0:03.94elapsed 48%CPU (0avgtext+0avgdata 7568maxresident)k 48inputs+131920outputs (1major+3562minor)pagefaults 0swaps ─────────────────────────────────────────────────────────────────────────────────────────── rick@alien:~/Downloads$ time (printf "Number Files: " && locate -cr $PWD".*\.c$") Number Files: 3523 real 0m0.775s user 0m0.766s sys 0m0.012s ─────────────────────────────────────────────────────────────────────────────────────────── rick@alien:~/Downloads$ time (printf "Number Dirs.: " && locate -r $PWD".*\.c$" | sed 's%/[^/]*$%/%' | uniq -c | wc -l) Number Dirs.: 648 real 0m0.778s user 0m0.788s sys 0m0.027s ───────────────────────────────────────────────────────────────────────────────────────────

Примечание. Это все файлы на ВСЕ приводах и разделах. т.е. мы можем также искать команды Windows:

$ time (printf "Number Files: " && locate *.exe -c) Number Files: 6541 real 0m0.946s user 0m0.761s sys 0m0.060s ─────────────────────────────────────────────────────────────────────────────────────────── rick@alien:~/Downloads$ time (printf "Number Dirs.: " && locate *.exe | sed 's%/[^/]*$%/%' | uniq -c | wc -l) Number Dirs.: 3394 real 0m0.942s user 0m0.803s sys 0m0.092s

У меня есть три раздела Windows NT NTFS, автоматически смонтированные в /etc/fstab.

Интересный подсчет:

$ time (printf "Number Files: " && locate / -c && printf "Number Dirs.: " && locate / | sed 's%/[^/]*$%/%' | uniq -c | wc -l) Number Files: 1637135 Number Dirs.: 286705 real 0m15.460s user 0m13.471s sys 0m2.786s

Требуется 15 секунд, чтобы посчитать 1637135 файлов в 286 705 каталогах. YMMV.

Для детальной разбивки на обработку регулярных выражений команды locate (кажется, не требуется в этом Q & amp; A, но используется на всякий случай), пожалуйста, прочтите следующее: Используйте & quot; locate & quot; в каком-то конкретном каталоге?

Дополнительная информация из последних статей:

sudo updatedb Обновить базу данных, используемую командой locate, если файлы .c были созданы сегодня или если вы HowtoForge - Linux Найдите команду для начинающих (8 примеров) locate -cr "$PWD.*\.c$" найдите все .c файлы в текущем каталоге и его дочерние элементы ($PWD). Вместо того, чтобы печатать имена файлов и распечатывать счетчик с аргументом -c. Параметр r задает регулярное выражение вместо соответствия по умолчанию *pattern*, которое может дать слишком много результатов.
2
ответ дан 17 July 2018 в 17:12

Вот мое предложение:

#!/bin/bash tempfile=$(mktemp) find -type f -name "*.c" -prune >$tempfile grep -c / $tempfile sed 's_[^/]*$__' $tempfile | sort -u | grep -c /

Этот короткий скрипт создает временный файл, находит каждый файл в и под текущим каталогом, заканчивающимся на .c, и записывает список в файл temp. grep затем используется для подсчета файлов (следуя Как я могу получить количество файлов в каталоге с помощью командной строки?) дважды: во второй раз каталоги, которые перечислены несколько раз, удаляются с помощью sort -u после удаления файлов из каждой строки, используя sed.

Это также корректно работает с новыми строками в именах файлов: grep -c / подсчитывает только строки с косой чертой и поэтому учитывает только первую строку многострочного имени файла в списке.

Выход

$ tree . ├── 1 │   ├── 1 │   │   ├── test2.c │   │   └── test.c │   └── 2 │   └── test.c └── 2    ├── 1    │   └── test.c    └── 2 $ tempfile=$(mktemp);find -type f -name "*.c" -prune >$tempfile;grep -c / $tempfile;sed 's_[^/]*$__' $tempfile | sort -u | grep -c / 4 3
4
ответ дан 23 July 2018 в 18:03

Python имеет os.walk, что делает такие задачи такими же легкими, интуитивно понятными и автоматически надежными даже перед лицом странных имен файлов, таких как те, которые содержат символы новой строки. Этот сценарий Python 3, который я изначально разместил в чате, предназначен для запуска в текущем каталоге (но он не должен находиться в текущем каталоге, и вы можете изменить его путь к os.walk), :

#!/usr/bin/env python3 import os dc = fc = 0 for _, _, fs in os.walk('.'): c = sum(f.endswith('.c') for f in fs) if c: dc += 1 fc += c print(dc, fc)

Это печатает количество каталогов, в которых содержится хотя бы один файл, имя которого заканчивается на .c, за которым следует пробел, за которым следует количество файлов, имена которых заканчиваются на .c , «Скрытые» файлы, то есть файлы, имена которых начинаются с ., включены, а скрытые каталоги аналогичным образом пересекаются.

os.walk Он перечисляет все каталоги, которые рекурсивно доступны из исходной точки, которую вы им даете, приводя информацию о каждом из них как кортеж из трех значений root, dirs, files. Для каждого каталога он переходит к (включая первый, имя которого вы даете ему):

root содержит путь к этому каталогу. Обратите внимание, что это абсолютно не связано с «корневым каталогом» системы / (а также не связано с /root), хотя оно будет идти к тем, если вы начнете там. В этом случае root начинается с пути . - i.e., Текущего каталога - и проходит повсюду под ним. dirs содержит список путей всех подкаталогов каталога, имя которого в настоящее время хранится в root. files содержит список путей всех файлов, которые находятся в каталоге, имя которого в настоящее время хранится в root, но которые не являются самими каталогами. Обратите внимание, что это включает в себя другие типы файлов, чем обычные файлы, включая символические ссылки, но похоже, что вы не ожидаете окончания таких записей в .c и заинтересованы в том, чтобы видеть что-либо подобное.

В этом случае мне нужно только изучить третий элемент кортежа, files (который я называю fs в скрипте). Как и команда find, Python os.walk перемещается в подкаталоги для меня; единственное, что я должен проверить сам, это имена файлов, в которые каждый из них содержится. В отличие от команды find, os.walk автоматически предоставляет мне список этих имен файлов.

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

Если вы когда-либо хотели os.walk следовать символическим ссылкам, что вы обычно не хотели, то вы можете передать followlinks=true на него. То есть вместо записи os.walk('.') вы можете написать os.walk('.', followlinks=true). Я повторяю, что вы редко этого хотели, особенно для такой задачи, в которой вы рекурсивно перечисляете всю структуру каталогов, независимо от того, насколько она велика, и подсчитывая все файлы в ней, которые удовлетворяют некоторым требованиям.

11
ответ дан 23 July 2018 в 18:03

Простой Perl один лайнер:

perl -MFile::Find=find -le'find(sub{/\.c\z/ and -f and $c{$File::Find::dir}=++$c}, @ARGV); print 0 + keys %c, " $c"' dir1 dir2

Или проще с командой find:

find dir1 dir2 -type f -name '*.c' -printf '%h\0' | perl -l -0ne'$c{$_}=1}{print 0 + keys %c, " $."'

Если вам нравится играть в гольф и иметь последние (например, менее десятилетия) Perl:

perl -MFile::Find=find -E'find(sub{/\.c$/&&-f&&($c{$File::Find::dir}=++$c)},".");say 0+keys%c," $c"' find -type f -name '*.c' -printf '%h\0'|perl -0nE'$c{$_}=1}{say 0+keys%c," $."'
4
ответ дан 23 July 2018 в 18:03

Маленький shellscript

Я предлагаю небольшой командный столбец bash с двумя основными командами (и переменной filetype, чтобы было легко переключиться, чтобы искать другие типы файлов).

Он не ищет или в символических ссылках, только обычные файлы.

#!/bin/bash filetype=c #filetype=pdf # count the 'filetype' files find -type f -name "*.$filetype" -ls|sed 's#.* \./##'|wc -l | tr '\n' ' ' # count directories containing 'filetype' files find -type d -exec bash -c "ls -AF '{}'|grep -e '\.'${filetype}$ -e '\.'${filetype}'\*'$ > /dev/null && echo '{} contains file(s)'" \;|grep 'contains file(s)$'|wc -l

Подробный shellscript

Это более подробная версия, которая также рассматривает символические ссылки,

#!/bin/bash filetype=c #filetype=pdf # counting the 'filetype' files echo -n "number of $filetype files in the current directory tree: " find -type f -name "*.$filetype" -ls|sed 's#.* \./##'|wc -l echo -n "number of $filetype symbolic links in the current directory tree: " find -type l -name "*.$filetype" -ls|sed 's#.* \./##'|wc -l echo -n "number of $filetype normal files in the current directory tree: " find -type f -name "*.$filetype" -ls|sed 's#.* \./##'|wc -l echo -n "number of $filetype symbolic links in the current directory tree including linked directories: " find -L -type f -name "*.$filetype" -ls 2> /tmp/c-counter |sed 's#.* \./##' | wc -l; cat /tmp/c-counter; rm /tmp/c-counter # list directories with and without 'filetype' files (good for manual checking; comment away after test) echo '---------- list directories:' find -type d -exec bash -c "ls -AF '{}'|grep -e '\.'${filetype}$ -e '\.'${filetype}'\*'$ > /dev/null && echo '{} contains file(s)' || echo '{} empty'" \; echo '' #find -L -type d -exec bash -c "ls -AF '{}'|grep -e '\.'${filetype}$ -e '\.'${filetype}'\*'$ > /dev/null && echo '{} contains file(s)' || echo '{} empty'" \; # count directories containing 'filetype' files echo -n "number of directories with $filetype files: " find -type d -exec bash -c "ls -AF '{}'|grep -e '\.'${filetype}$ -e '\.'${filetype}'\*'$ > /dev/null && echo '{} contains file(s)'" \;|grep 'contains file(s)$'|wc -l # list and count directories including symbolic links, containing 'filetype' files echo '---------- list all directories including symbolic links:' find -L -type d -exec bash -c "ls -AF '{}' |grep -e '\.'${filetype}$ -e '\.'${filetype}'\*'$ > /dev/null && echo '{} contains file(s)' || echo '{} empty'" \; echo '' echo -n "number of directories (including symbolic links) with $filetype files: " find -L -type d -exec bash -c "ls -AF '{}'|grep -e '\.'${filetype}$ -e '\.'${filetype}'\*'$ > /dev/null && echo '{} contains file(s)'" \; 2>/dev/null |grep 'contains file(s)$'|wc -l # count directories without 'filetype' files (good for checking; comment away after test) echo -n "number of directories without $filetype files: " find -type d -exec bash -c "ls -AF '{}'|grep -e '\.'${filetype}$ -e '\.'${filetype}'\*'$ > /dev/null || echo '{} empty'" \;|grep 'empty$'|wc -l

Тестовый выход

Из короткого shellscript:

$ ./ccntr 29 7

Из подробного shellscript:

$ LANG=C ./c-counter number of c files in the current directory tree: 29 number of c symbolic links in the current directory tree: 1 number of c normal files in the current directory tree: 29 number of c symbolic links in the current directory tree including linked directories: 42 find: './cfiles/2/2': Too many levels of symbolic links find: './cfiles/dirlink/2': Too many levels of symbolic links ---------- list directories: . empty ./cfiles contains file(s) ./cfiles/2 contains file(s) ./cfiles/2/b contains file(s) ./cfiles/2/a contains file(s) ./cfiles/3 empty ./cfiles/3/b contains file(s) ./cfiles/3/a empty ./cfiles/1 contains file(s) ./cfiles/1/b empty ./cfiles/1/a empty ./cfiles/space d contains file(s) number of directories with c files: 7 ---------- list all directories including symbolic links: . empty ./cfiles contains file(s) ./cfiles/2 contains file(s) find: './cfiles/2/2': Too many levels of symbolic links ./cfiles/2/b contains file(s) ./cfiles/2/a contains file(s) ./cfiles/3 empty ./cfiles/3/b contains file(s) ./cfiles/3/a empty ./cfiles/dirlink empty find: './cfiles/dirlink/2': Too many levels of symbolic links ./cfiles/dirlink/b contains file(s) ./cfiles/dirlink/a contains file(s) ./cfiles/1 contains file(s) ./cfiles/1/b empty ./cfiles/1/a empty ./cfiles/space d contains file(s) number of directories (including symbolic links) with c files: 9 number of directories without c files: 5 $
4
ответ дан 23 July 2018 в 18:03

Найти + Perl:

$ find . -type f -iname '*.c' -printf '%h\0' | perl -0 -ne '$k{$_}++; }{ print scalar keys %k, " $.\n" ' 7 29

Объяснение

Команда find найдет любые обычные файлы (так что никаких символических ссылок или каталогов), а затем напечатать имя каталога, которое они находятся в (%h), а затем \0.

perl -0 -ne: прочитайте ввод строки за строкой (-n) и примените скрипт, заданный -e для каждой строки. [F9] устанавливает разделитель входных строк на \0, чтобы мы могли считывать ввод с нулевым ограничением. $k{$_}++: $_ - специальная переменная, которая принимает значение текущей строки. Это используется как ключ к хешу %k, значения которого представляют собой количество раз, когда была видна каждая строка ввода (имя каталога). }{: это сокращенный способ написания END{}. Любые команды после }{ будут выполняться один раз, после того, как все данные будут обработаны. print scalar keys %k, " $.\n": keys %k возвращает массив ключей в хеше %k. scalar keys %k дает количество элементов в этом массиве, количество просмотренных каталогов. Это печатается вместе с текущим значением $., специальной переменной, которая содержит текущий номер строки ввода. Так как это выполняется в конце, текущий номер строки ввода будет номером последней строки, поэтому количество строк, видимых до сих пор.

Вы можете расширить команду perl для этого, для ясности:

find . -type f -iname '*.c' -printf '%h\0' | perl -0 -e 'while($line = <STDIN>){ $dirs{$line}++; $tot++; } $count = scalar keys %dirs; print "$count $tot\n" '
7
ответ дан 23 July 2018 в 18:03

Используйте команду locate, которая намного быстрее, чем команда find.

Выполнение тестовых данных

$ sudo updatedb # necessary if files in focus were added `cron` daily. $ printf "Number Files: " && locate -0r "$PWD.*\.c$" | xargs -0 -I{} sh -c 'test ! -L "$1" && echo "regular file"' _ {} | wc -l && printf "Number Dirs.: " && locate -r "$PWD.*\.c$" | sed 's%/[^/]*$%/%' | uniq -cu | wc -l Number Files: 29 Number Dirs.: 7

Спасибо Муру за его ответ, чтобы помочь мне путем удаления символических ссылок из количества файлов в Unix & amp; Ответ на Linux.

Спасибо Тердону за его ответ $PWD (не направленный на меня) в Unix & amp; Ответ на Linux .

Оригинальный ответ ниже, на который ссылаются комментарии

Короткая форма:

$ cd / $ sudo updatedb $ printf "Number Files: " && locate -cr "$PWD.*\.c$" Number Files: 3523 $ printf "Number Dirs.: " && locate -r "$PWD.*\.c$" | sed 's%/[^/]*$%/%' | uniq -c | wc -l Number Dirs.: 648 sudo updatedb Обновить базу данных, используемую командой locate, если сегодня были созданы файлы .c или если вы удалили файлы .c сегодня. locate -cr "$PWD.*\.c$" найдите все .c файлы в текущем каталоге и его дочерние элементы ($PWD). Вместо того, чтобы печатать имена файлов и распечатывать счетчик с аргументом -c. Параметр r задает регулярное выражение вместо соответствия по умолчанию *pattern*, которое может дать слишком много результатов. [F20]. Найдите все *.c файлы в текущем каталоге и ниже. Удалите имя файла с sed, оставив только имя каталога. Подсчитайте количество файлов в каждом каталоге с помощью uniq -c. Подсчитайте количество каталогов с помощью wc -l.

Начать с текущего каталога с помощью одного слоя

$ cd /usr/src $ printf "Number Files: " && locate -cr "$PWD.*\.c$" && printf "Number Dirs.: " && locate -r "$PWD.*\.c$" | sed 's%/[^/]*$%/%' | uniq -c | wc -l Number Files: 3430 Number Dirs.: 624

Обратите внимание, что количество файлов и количество каталогов изменились. Я полагаю, что все пользователи имеют каталог /usr/src и могут работать над командами с разными значениями в зависимости от количества установленных ядер.

Длинная форма:

Длинная форма включает время, чтобы вы может видеть, насколько быстрее locate закончилось find. Даже если вам нужно запустить sudo updatedb, он во много раз быстрее, чем один find /.

─────────────────────────────────────────────────────────────────────────────────────────── rick@alien:~/Downloads$ sudo time updatedb 0.58user 1.32system 0:03.94elapsed 48%CPU (0avgtext+0avgdata 7568maxresident)k 48inputs+131920outputs (1major+3562minor)pagefaults 0swaps ─────────────────────────────────────────────────────────────────────────────────────────── rick@alien:~/Downloads$ time (printf "Number Files: " && locate -cr $PWD".*\.c$") Number Files: 3523 real 0m0.775s user 0m0.766s sys 0m0.012s ─────────────────────────────────────────────────────────────────────────────────────────── rick@alien:~/Downloads$ time (printf "Number Dirs.: " && locate -r $PWD".*\.c$" | sed 's%/[^/]*$%/%' | uniq -c | wc -l) Number Dirs.: 648 real 0m0.778s user 0m0.788s sys 0m0.027s ───────────────────────────────────────────────────────────────────────────────────────────

Примечание. Это все файлы на ВСЕ приводах и разделах. т.е. мы можем также искать команды Windows:

$ time (printf "Number Files: " && locate *.exe -c) Number Files: 6541 real 0m0.946s user 0m0.761s sys 0m0.060s ─────────────────────────────────────────────────────────────────────────────────────────── rick@alien:~/Downloads$ time (printf "Number Dirs.: " && locate *.exe | sed 's%/[^/]*$%/%' | uniq -c | wc -l) Number Dirs.: 3394 real 0m0.942s user 0m0.803s sys 0m0.092s

У меня есть три раздела Windows NT NTFS, автоматически смонтированные в /etc/fstab.

Интересный подсчет:

$ time (printf "Number Files: " && locate / -c && printf "Number Dirs.: " && locate / | sed 's%/[^/]*$%/%' | uniq -c | wc -l) Number Files: 1637135 Number Dirs.: 286705 real 0m15.460s user 0m13.471s sys 0m2.786s

Требуется 15 секунд, чтобы посчитать 1637135 файлов в 286 705 каталогах. YMMV.

Для детальной разбивки на обработку регулярных выражений команды locate (кажется, не требуется в этом Q & amp; A, но используется на всякий случай), пожалуйста, прочтите следующее: Используйте & quot; locate & quot; в каком-то конкретном каталоге?

Дополнительная информация из последних статей:

sudo updatedb Обновить базу данных, используемую командой locate, если файлы .c были созданы сегодня или если вы HowtoForge - Linux Найдите команду для начинающих (8 примеров) locate -cr "$PWD.*\.c$" найдите все .c файлы в текущем каталоге и его дочерние элементы ($PWD). Вместо того, чтобы печатать имена файлов и распечатывать счетчик с аргументом -c. Параметр r задает регулярное выражение вместо соответствия по умолчанию *pattern*, которое может дать слишком много результатов.
2
ответ дан 23 July 2018 в 18:03
  • 1
    Это не учитывает файлы в определенном каталоге. Как вы заметили, он подсчитывает все файлы (или каталоги или любой другой тип файла), соответствующие .c (обратите внимание, что он будет разбит, если в текущем каталоге есть файл с именем -.c, поскольку вы не цитируете *.c), а затем распечатает все каталоги в системе, независимо от того, содержат ли они файлы .c. – terdon♦ 10 April 2018 в 12:01
  • 2
    @terdon Вы можете передать каталог ~/my_c_progs/*.c. Он подсчитывает 638 каталогов с программами .c, общие каталоги отображаются позже как 286,705. Я переработаю ответ на двойную цитату `& quot; *. C & quot ;. Спасибо за совет. – WinEunuuchs2Unix 10 April 2018 в 13:03
  • 3
    Да, вы можете использовать что-то вроде locate -r "/path/to/dir/.*\.c$", но это не упоминается нигде в вашем ответе. Вы даете ссылку на другой ответ, который упоминает об этом, но без объяснения того, как его адаптировать, чтобы ответить на вопрос, задаваемый здесь. Весь ваш ответ сосредоточен на том, как подсчитать общее количество файлов и каталогов в системе, что не имеет отношения к заданному вопросу, который был «как я могу подсчитать количество файлов .c и количество каталогов, содержащих .c файлы в определенном каталоге ". Кроме того, ваши цифры неверны, попробуйте его на примере в OP. – terdon♦ 10 April 2018 в 13:45
  • 4
    @terdon Спасибо за ваш вклад. Я улучшил ответ с вашими предложениями и ответом, который вы разместили на другом сайте SE для переменной $PWD: unix.stackexchange.com/a/188191/200094 – WinEunuuchs2Unix 11 April 2018 в 03:45
  • 5
    Теперь вы должны убедиться, что $PWD не содержит символов, которые могут быть специальными в регулярном выражении – muru 11 April 2018 в 03:55

Другие вопросы по тегам:

Похожие вопросы: