Есть ли команда bash, которая подсчитывает количество файлов, которые соответствуют шаблону?
Например, я хочу получить количество всех файлов в каталоге, которые соответствуют этому шаблону: log*
Есть ли команда bash, которая подсчитывает количество файлов, которые соответствуют шаблону?
Например, я хочу получить количество всех файлов в каталоге, которые соответствуют этому шаблону: log*
Этот простой однострочный слой должен работать в любой оболочке, а не только bash:
ls -1q log* | wc -l
ls -1q даст вам одну строку на файл, даже если они содержат пробелы или специальные символы, такие как символы новой строки.
Выход поступает на wc -l, который подсчитывает количество строк.
Вы можете сделать это безопасно (т.е. Не будут прослушиваться файлами с пробелами или \n
в их имени) с помощью bash:
$ shopt -s nullglob
$ logfiles=(*.log)
$ echo ${#logfiles[@]}
Вам нужно включить nullglob
чтобы вы не получили литерал *.log
в массиве $logfiles
если не найдено ни одного файла. (См. Как "отменить" 'set -x'?? Для примеров того, как безопасно сбросить его.)
Здесь много ответов, но некоторые не принимают во внимание
-l
)*.log
вместо log*
logs
который соответствует log*
)Вот решение, которое обрабатывает все из них:
ls 2>/dev/null -Ubad1 -- log* | wc -l
Объяснение:
-U
заставляет ls
не сортировать записи, что означает, что ему не нужно загружать весь список каталогов в память-b
печатает C -s экранированные символы для неграфических символов, что принципиально приводит к тому, что переводы новой строки печатаются как \n
.-a
распечатывает все файлы, даже скрытые (не требуется строго, если в log*
глобуса log*
нет скрытых файлов)-d
распечатывает каталоги, не пытаясь -d
список содержимого каталога, что обычно делает ls
-1
, находится ли он в одном столбце (ls делает это автоматически при записи в канал, поэтому это не является строго обязательным)2>/dev/null
перенаправляет stderr, чтобы при наличии 0 файлов журнала игнорировать сообщение об ошибке. (Обратите внимание, что shopt -s nullglob
приведет к тому, что ls
shopt -s nullglob
список всего рабочего каталога.)wc -l
использует список каталогов по мере его создания, поэтому вывод ls
никогда не находится в памяти в любой момент времени.--
Имена файлов отделяются от команды с помощью --
чтобы не быть понятыми как аргументы для ls
(в случае, если log*
удален) Оболочка расширит log*
до полного списка файлов, которые могут исчерпать память, если в ней много файлов, поэтому лучше запустить ее через grep:
ls -Uba1 | grep ^log | wc -l
Последний обрабатывает очень большие каталоги файлов без использования большого количества памяти (хотя и использует подоболочку). -d
больше не нужен, потому что он только перечисляет содержимое текущего каталога.
Для рекурсивного поиска:
find . -type f -name '*.log' -printf x | wc -c
wc -c
будет подсчитывать количество символов в выходных данных find
, а -printf x
предписывает find
печатать по одному x
для каждого результата.
Для нерекурсивного поиска сделайте следующее:
find . -maxdepth 1 -type f -name '*.log' -printf x | wc -c
Принятый ответ на этот вопрос неверен, но у меня низкий показатель, поэтому он не может добавить комментарий к нему.
Правильный ответ на этот вопрос задается Mat:
shopt -s nullglob
logfiles=(*.log)
echo ${#logfiles[@]}
Проблема с принятым ответом заключается в том, что wc -l подсчитывает количество символов новой строки и подсчитывает их, даже если они печатают на терминал как '?' в выводе 'ls -l'. Это означает, что принятый ответ FAILS, когда имя файла содержит символ новой строки. Я проверил предложенную команду:
ls -l log* | wc -l
и он ошибочно сообщает значение 2, даже если есть только один файл, соответствующий шаблону, имя которого содержит символ новой строки. Например:
touch log$'\n'def
ls log* -l | wc -l
Если у вас много файлов, и вы не хотите использовать элегантное решение массива shopt -s nullglob
и bash, вы можете использовать find и т.д., пока вы не распечатываете имя файла ( который может содержать символы новой строки).
find -maxdepth 1 -name "log*" -not -name ".*" -printf '%i\n' | wc -l
Это найдет все файлы, которые соответствуют журналу *, и которые не начинаются с .*
. "Не имя. *" является избыточным, но важно отметить, что по умолчанию для "ls" следует не показывать точку файлы, но по умолчанию для поиска следует включить их.
Это правильный ответ и обрабатывает любой тип имени файла, который вы можете наложить на него, потому что имя файла никогда не передается между командами.
Но ответ shopt nullglob
- лучший ответ!
Вот мой один лайнер для этого.
file_count=$( shopt -s nullglob ; set -- $directory_to_search_inside/* ; echo $#)
Вы можете использовать опцию -R, чтобы найти файлы вместе с файлами внутри рекурсивных каталогов.
ls -R | wc -l // to find all the files
ls -R | grep php | wc -l // to find the files which contains the word php
Вы можете использовать шаблоны на grep
Вы можете легко определить такую команду, используя функцию оболочки. Этот метод не требует никакой внешней программы и не порождает дочерний процесс. Он не пытается выполнить опасный анализ ls
и обрабатывает "специальные" символы (пробелы, переводы строки, обратную косую черту и т.д.) Просто отлично. Он опирается только на механизм расширения имени файла, предоставляемый оболочкой. Он совместим как минимум с sh, bash и zsh.
Строка ниже определяет функцию с именем count
, которая печатает количество аргументов, с которыми она была вызвана.
count() { echo $#; }
Просто позвоните по нужному шаблону:
count log*
Чтобы результат был корректным, когда шаблон глобирования не совпадает, параметр оболочки nullglob
(или failglob
- это поведение по умолчанию для zsh) должен быть установлен во время расширения. Это можно установить так:
shopt -s nullglob # for sh / bash
setopt nullglob # for zsh
В зависимости от того, что вы хотите считать, вас также может заинтересовать опция оболочки dotglob
.
К сожалению, по крайней мере, с bash, нелегко установить эти параметры локально. Если вы не хотите устанавливать их глобально, самое простое решение - использовать эту функцию более замысловато:
( shopt -s nullglob ; shopt -u failglob ; count log* )
Если вы хотите восстановить упрощенный синтаксис count log*
или если вы действительно хотите избежать появления подоболочки, вы можете взломать что-то вроде:
# sh / bash:
# the alias is expanded before the globbing pattern, so we
# can set required options before the globbing gets expanded,
# and restore them afterwards.
count() {
eval "$_count_saved_shopts"
unset _count_saved_shopts
echo $#
}
alias count='
_count_saved_shopts="$(shopt -p nullglob failglob)"
shopt -s nullglob
shopt -u failglob
count'
В качестве бонуса эта функция имеет более общее использование. Например:
count a* b* # count files which match either a* or b*
count $(jobs -ps) # count stopped jobs (sh / bash)
Превратив функцию в файл сценария (или эквивалентную программу на C), вызываемую из PATH, ее также можно составить с помощью таких программ, как find
и xargs
:
find "$FIND_OPTIONS" -exec count {} \+ # count results of a search
ls -1 log* | wc -l
Что означает список по одному файлу на строку, а затем передать его в команду подсчета слов с переключением параметров для подсчета строк.
Вот что я всегда делаю:
ls log * | awk 'END {print NR}'