Есть ли команда bash, которая подсчитывает файлы?

Есть ли команда bash, которая подсчитывает количество файлов, которые соответствуют шаблону?

Например, я хочу получить количество всех файлов в каталоге, которые соответствуют этому шаблону: log*

Ответ 1

Этот простой однострочный слой должен работать в любой оболочке, а не только bash:

ls -1q log* | wc -l

ls -1q даст вам одну строку на файл, даже если они содержат пробелы или специальные символы, такие как символы новой строки.

Выход поступает на wc -l, который подсчитывает количество строк.

Ответ 2

Вы можете сделать это безопасно (т.е. Не будут прослушиваться файлами с пробелами или \n в их имени) с помощью bash:

$ shopt -s nullglob
$ logfiles=(*.log)
$ echo ${#logfiles[@]}

Вам нужно включить nullglob чтобы вы не получили литерал *.log в массиве $logfiles если не найдено ни одного файла. (См. Как "отменить" 'set -x'?? Для примеров того, как безопасно сбросить его.)

Ответ 3

Здесь много ответов, но некоторые не принимают во внимание

  • имена файлов с пробелами, символами новой строки или управляющими символами в них
  • имена файлов, начинающиеся с дефисов (представьте файл с именем -l)
  • скрытые файлы, которые начинаются с точки (если глобус был *.log вместо log*
  • каталоги, которые соответствуют глобу (например, каталог с именем logs который соответствует log*)
  • пустые каталоги (т.е. результат равен 0)
  • очень большие каталоги (перечисление их всех может исчерпать память)

Вот решение, которое обрабатывает все из них:

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 больше не нужен, потому что он только перечисляет содержимое текущего каталога.

Ответ 4

Для рекурсивного поиска:

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

Ответ 5

Принятый ответ на этот вопрос неверен, но у меня низкий показатель, поэтому он не может добавить комментарий к нему.

Правильный ответ на этот вопрос задается 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

Ответ 6

Если у вас много файлов, и вы не хотите использовать элегантное решение массива shopt -s nullglob и bash, вы можете использовать find и т.д., пока вы не распечатываете имя файла ( который может содержать символы новой строки).

find -maxdepth 1 -name "log*" -not -name ".*" -printf '%i\n' | wc -l

Это найдет все файлы, которые соответствуют журналу *, и которые не начинаются с .*. "Не имя. *" является избыточным, но важно отметить, что по умолчанию для "ls" следует не показывать точку файлы, но по умолчанию для поиска следует включить их.

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

Но ответ shopt nullglob - лучший ответ!

Ответ 7

Вот мой один лайнер для этого.

 file_count=$( shopt -s nullglob ; set -- $directory_to_search_inside/* ; echo $#)

Ответ 8

Вы можете использовать опцию -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

Ответ 9

Вы можете легко определить такую команду, используя функцию оболочки. Этот метод не требует никакой внешней программы и не порождает дочерний процесс. Он не пытается выполнить опасный анализ 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

Ответ 10

ls -1 log* | wc -l

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

Ответ 11

Вот что я всегда делаю:

ls log * | awk 'END {print NR}'