Ответы:
Этот простой однострочный текст должен работать в любой оболочке, а не только в bash:
ls -1q log* | wc -l
ls -1q выдаст вам по одной строке на файл, даже если они содержат пробелы или специальные символы, такие как символы новой строки.
Выходные данные передаются в wc -l, который считает количество строк.
ls, так как он создает дочерний процесс. log*расширяется оболочкой, нет ls, так что простой echoбудет делать.
logs, то будет учитываться и содержимое этого каталога журналов. Это, вероятно, не намеренно.
Вы можете сделать это безопасно (то есть не будете ошибаться файлами с пробелами или \nих именами) с помощью bash:
$ shopt -s nullglob
$ logfiles=(*.log)
$ echo ${#logfiles[@]}
Вам нужно включить, nullglobчтобы вы не получили литерал *.logв $logfiles массиве, если не найдено ни одного файла. (См. Как «отменить» set -x для примеров того, как безопасно сбросить его.)
shopt -u nullglobдолжен быть пропущен, если nullglobон не был сброшен, тогда вы начали.
*.logтолько *будет считать каталоги. Если файлы, которые вы хотите перечислить, имеют традиционное соглашение об именах name.extension, используйте *.*.
Здесь много ответов, но некоторые не принимают во внимание
-l)*.logвместоlog*logsкоторый соответствует log*)Вот решение, которое обрабатывает все из них:
ls 2>/dev/null -Ubad1 -- log* | wc -l
Объяснение:
-Uвызывает lsне сортировать записи, то есть не нужно загружать весь список каталогов в память-bпечатает экранирование в стиле C для неграфических символов, что принципиально приводит к тому, что переводы строк печатаются как \n.-aраспечатывает все файлы, даже скрытые файлы (не требуется строго, когда глобус не log*подразумевает скрытых файлов)-dраспечатывает каталоги, не пытаясь составить список содержимого каталога, что lsобычно делается-1 удостоверяется, что он находится в одном столбце (ls делает это автоматически при записи в канал, поэтому это не является строго обязательным)2>/dev/nullперенаправляет stderr, чтобы при наличии 0 файлов журнала игнорировать сообщение об ошибке. (Обратите внимание, что вместо этого shopt -s nullglobможет lsбыть указан весь рабочий каталог.)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
-name '*.log'он будет считать все файлы, что мне и понадобилось для моего варианта использования. Также флаг -maxdepth чрезвычайно полезен, спасибо!
find; просто напечатайте что-то еще, кроме имени файла.
Принятый ответ на этот вопрос неправильный, но у меня низкая репутация, поэтому я не могу добавить к нему комментарий.
Правильный ответ на этот вопрос дает Мат:
shopt -s nullglob
logfiles=(*.log)
echo ${#logfiles[@]}
Проблема с принятым ответом состоит в том, что wc -l считает количество символов новой строки и считает их, даже если они выводят на терминал как «?» в выводе 'ls -l'. Это означает, что принятый ответ НЕУДАЧЕТ, когда имя файла содержит символ новой строки. Я проверил предложенную команду:
ls -l log* | wc -l
и он ошибочно сообщает значение 2, даже если существует только 1 файл, соответствующий шаблону, имя которого содержит символ новой строки. Например:
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
Это найдет все файлы, которые соответствуют log * и не начинаются с .* - «not name. *» Избыточно, но важно отметить, что по умолчанию для «ls» не показываются точечные файлы, но по умолчанию для поиска, чтобы включить их.
Это правильный ответ, и он обрабатывает любое имя файла, которое вы можете использовать, потому что имя файла никогда не передается между командами.
Но shopt nullglobответ - лучший ответ!
findпротив использования lsдвух разных способов решения проблемы. findне всегда присутствует на машине, но lsобычно есть,
findвероятно, не имеет всех этих причудливых вариантов lsни для одного.
-maxdepth 1
findделает это по умолчанию. Это может создать путаницу, если вы не поймете, что есть скрытая дочерняя папка, и может сделать его выгодным lsв некоторых случаях, когда по умолчанию не отображаются скрытые файлы.
Вот мой единственный вкладыш для этого.
file_count=$( shopt -s nullglob ; set -- $directory_to_search_inside/* ; echo $#)
set -- что ничего не делает, кроме как подготовить нас к тому $#, что хранит количество аргументов командной строки, которые были переданы программе оболочки
(недостаточно репутации, чтобы комментировать)
Это БАГГИ :
ls -1q some_pattern | wc -l
Если shopt -s nullglobон установлен, он печатает количество ВСЕХ обычных файлов, а не только файлы с шаблоном (протестировано на CentOS-8 и Cygwin). Кто знает, что имеют другие бессмысленные ошибки ls?
Это ПРАВИЛЬНО и намного быстрее:
shopt -s nullglob; files=(some_pattern); echo ${#files[@]};
Это делает ожидаемую работу.
0.006на CentOS и 0.083Cygwin (если он используется с осторожностью).
0.000на CentOS и 0.003на Cygwin.
Вы можете легко определить такую команду, используя функцию оболочки. Этот метод не требует никакой внешней программы и не порождает дочерний процесс. Он не пытается выполнить опасный 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
Я много думал об этом ответе, особенно учитывая материал не-parse-ls . Сначала я попробовал
<ВНИМАНИЕ! НЕ РАБОТАЕТ>
du --inodes --files0-from=<(find . -maxdepth 1 -type f -print0) | awk '{sum+=int($1)}END{print sum}'
</ ВНИМАНИЕ! НЕ РАБОТАЕТ>
который работал, если было только имя файла, как
touch $'w\nlf.aa'
но не удалось, если я сделал имя файла, как это
touch $'firstline\n3 and some other\n1\n2\texciting\n86stuff.jpg'
Я наконец-то придумал, что я ставлю ниже. Обратите внимание, что я пытался получить количество всех файлов в каталоге (не включая какие-либо подкаталоги). Я думаю, что наряду с ответами @Mat и @Dan_Yard, а также наличием по крайней мере большинства требований, изложенных @mogsie (я не уверен насчет памяти.) Я думаю, что ответ @mogsie правильный, но я всегда стараюсь держаться подальше от разбора, lsесли это не очень специфическая ситуация.
awk -F"\0" '{print NF-1}' < <(find . -maxdepth 1 -type f -print0) | awk '{sum+=$1}END{print sum}'
Более читабельно:
awk -F"\0" '{print NF-1}' < \
<(find . -maxdepth 1 -type f -print0) | \
awk '{sum+=$1}END{print sum}'
Это делается для поиска файлов, ограничивая вывод нулевым символом (чтобы избежать проблем с пробелами и переводами строк), а затем подсчитывает количество нулевых символов. Количество файлов будет на один меньше количества нулевых символов, так как в конце будет нулевой символ.
Чтобы ответить на вопрос ФП, необходимо рассмотреть два случая.
1) нерекурсивный поиск:
awk -F"\0" '{print NF-1}' < \
<(find . -maxdepth 1 -type f -name "log*" -print0) | \
awk '{sum+=$1}END{print sum}'
2) Рекурсивный поиск. Обратите внимание, что то, что находится внутри -nameпараметра, может потребоваться изменить для немного другого поведения (скрытые файлы и т. Д.).
awk -F"\0" '{print NF-1}' < \
<(find . -type f -name "log*" -print0) | \
awk '{sum+=$1}END{print sum}'
Если кто-то хотел бы прокомментировать, как эти ответы сравниваются с теми, которые я упомянул в этом ответе, сделайте это.
Обратите внимание, я получил этот мыслительный процесс, получая этот ответ .
ls -1 log* | wc -l
Это означает, что нужно перечислить один файл в строке, а затем передать его команде word count с переключением параметров на счетчик строк.
-l, поскольку это требуетstat(2)для каждого файла и для целей подсчета ничего не добавляет.