Почему вы хотите использовать, findкогда Bash будет делать? (shopt -s dotglob; for dir in */; do all=("$dir"/*); echo "$dir: ${#all[@]}"; done): для всех каталогов подсчитайте количество записей в этом каталоге (включая файлы со скрытыми точками, исключая .и ..)
января
@janmoesen Почему ты не ответил? Я новичок в сценариях оболочки, но я не вижу никаких ошибок с вашим методом. Для меня это выглядит как лучший способ. Никто не проголосовал за ваш комментарий, но никто не прокомментировал, почему это может быть плохо. Ответы, на которые проголосовали, имеют гораздо больше повторений, чем вы, так что меня удивляет, что я что-то упустил
Токсалот
@toxalot: я не удосужился добавить его в качестве ответа, потому что он был очень коротким (и, возможно, слегка снисходительным по тону). Не стесняйтесь поднять комментарий. :-) Кроме того, вопрос несколько неясен в отношении того, что означает «сколько файлов». Мое решение считает "обычные" файлы и каталоги; может быть, на самом деле плакат означал «файлы, а не каталоги». Еще одна вещь, которую нужно иметь в виду, заключается в том, что эта глобализация не учитывает «скрытые» файлы точек. Тем не менее, есть способы обойти обе эти проблемы. Но опять же: не уверен в точных требованиях оригинального плаката.
Янмезен
Ответы:
30
Это делает это безопасным и портативным способом. Это не запутает странные имена файлов.
for f in *; do [ -d ./"$f" ] && find ./"$f" -maxdepth 1 -exec echo \; | wc -l && echo $f; done
Обратите внимание, что сначала будет напечатано количество файлов, а затем имя каталога в отдельной строке. Если вы хотите сохранить формат OP, вам понадобится дополнительное форматирование, например
for f in *; do [ -d ./"$f" ] && find ./"$f" -maxdepth 1 -exec echo \;|wc -l|tr '\n' ' ' && echo $f; done|awk '{print $2"\t"$1}'
Если у вас есть определенный набор подкаталогов, которые вас интересуют, вы можете заменить их на *них.
Почему это безопасно? (и поэтому достойный скрипта)
Имена файлов могут содержать любые символы, кроме /. Есть несколько символов, которые обрабатываются специально либо оболочкой, либо командами. К ним относятся пробелы, переводы строки и тире.
Использование for f in *конструкции - это безопасный способ получить каждое имя файла, независимо от того, что оно содержит.
Если у вас есть имя файла в переменной, вы все равно должны избегать таких вещей, как find $f. Если бы $fсодержалось имя файла -test, вы findбы пожаловались на вариант, который вы только что дали. Чтобы избежать этого, используйте ./перед именем; таким образом, он имеет то же значение, но больше не начинается с тире.
Новые строки и пробелы также являются проблемой. Если $fв качестве имени файла содержится "привет, приятель" find ./$f, есть find ./hello, buddy. Вы говорите, findчтобы посмотреть ./hello,и buddy. Если таковых не существует, он будет жаловаться и никогда не заглядывает внутрь ./hello, buddy. Этого легко избежать - используйте кавычки вокруг ваших переменных.
Наконец, имена файлов могут содержать новые строки, поэтому подсчет новых строк в списке имен файлов не будет работать; Вы получите дополнительный счет для каждого имени файла с новой строкой. Чтобы избежать этого, не считайте новые строки в списке файлов; вместо этого подсчитайте переводы строки (или любой другой символ), которые представляют один файл. Вот почему findкоманда просто -exec echo \;и нет -exec echo {} \;. Я хочу напечатать только одну новую строку для подсчета файлов.
Количество будет включать в себя сам каталог. Если вы хотите исключить это из подсчета, используйте-mindepth 1
toxalot
Вы также можете использовать -printf '\n'вместо -exec echo.
toxalot
1
@toxalot вы можете, если у вас есть находка, которая поддерживает -printf, но не если вы хотите, чтобы она работала, например, на FreeBSD.
Шон Дж. Гофф
6
Предполагая, что вы ищете стандартное решение Linux, сравнительно простой способ добиться этого заключается в find:
find dir1/ dir2/ -maxdepth 1 -type f | wc -l
Где findпересекает две указанные подкаталоги, в -maxdepth1, что предотвращает дальнейшую рекурсию и сообщает только файлы ( -type f), разделенные символами новой строки. Затем результат передается для wcподсчета количества этих строк.
У меня более 2 dirs ... Как я могу объединить вашу команду с find . -maxdepth 1 -type dвыводом?
ShyBoy
Вы можете (а) включить необходимые переменные в переменную и, find $dirs ...или (б) если они находятся исключительно в одном каталоге более высокого уровня, глобус из этого каталога,find */ ...
jasonwryan
1
Это сообщит о неверных результатах, если любое имя файла содержит символ новой строки.
Шон Дж. Гофф
@Shawn: спасибо. Я думал, что у меня есть имена файлов с пробелами, но не рассматривал новые строки: какие-либо предложения по исправлению?
Джейсонвриан
Добавьте -exec echoк вашей команде поиска - таким образом, оно не отображает имя файла, просто перевод строки.
Шон Дж. Гофф
5
Под «без рекурсии» вы имеете в виду, что если directoryName1есть подкаталоги, то вы не хотите считать файлы в подкаталогах? Если это так, вот способ подсчитать все обычные файлы в указанных каталогах:
count=0
for d in directoryName1 directoryName2; do
for f in "$d"/* "$d"/.[!.]* "$d"/..?*; do
if [ -f "$f" ]; then count=$((count+1)); fi
done
done
Обратите внимание, что -fтест выполняет две функции: он проверяет, является ли запись, совпадающая с одним из указанных выше глобусов, обычным файлом, и проверяет, была ли запись совпадающей (если один из глобусов ничего не соответствует, шаблон остается как есть). Если вы хотите сосчитать все записи в указанных каталогах независимо от их типа, замените -fна -e.
В Ksh есть способ сделать шаблоны соответствующими точечным файлам и создать пустой список в случае, если ни один файл не соответствует шаблону. Таким образом, в ksh вы можете считать обычные файлы следующим образом:
FIGNORE='.?(.)'
count=0
for x in ~(N)directoryName1/* ~(N)directoryName2/*; do
if [ -f "$x" ]; then ((++count)); fi
done
¹ Обратите внимание, что каждый шаблон соответствует самому себе, в противном случае результаты могут быть отключены (например, если вы считаете файлы, начинающиеся с цифры, вы не можете просто сделать это, for x in [0-9]*; do if [ -f "$x" ]; then …потому что может быть файл с именем [0-9]foo).
Основываясь на сценарии подсчета , ответе Шона и трюке Bash, чтобы убедиться, что даже имена файлов с символами новой строки напечатаны в удобной форме в одну строку:
for f in *
do
if [ -d "./$f" ]
then
printf %q "$f"
printf %s ' '
find "$f" -maxdepth 1 -printf x | wc -c
fi
done
printf %qзаключается в печати цитируемой версии строки, то есть строки в одну строку, которую вы можете поместить в скрипт Bash, чтобы интерпретировать ее как буквенную строку, включающую (потенциально) переводы строки и другие специальные символы. Например, см. echo -n $'\tfoo\nbar'Против printf %q $'\tfoo\nbar'.
Команда findработает, просто печатая один символ для каждого файла, а затем подсчитывая их вместо подсчета строк.
Добро пожаловать в U & L. Ответы должны быть подробными с объяснениями, а не просто пропусками кода. Пожалуйста, раскройте это и объясните, что происходит. Кроме того, это очень неэффективный способ сделать это и не обрабатывать файлы с пробелами, например.
SLM
-1
for i in `ls -1`; do echo $i : `ls -1 $i|wc -l`; done
find
когда Bash будет делать?(shopt -s dotglob; for dir in */; do all=("$dir"/*); echo "$dir: ${#all[@]}"; done)
: для всех каталогов подсчитайте количество записей в этом каталоге (включая файлы со скрытыми точками, исключая.
и..
)Ответы:
Это делает это безопасным и портативным способом. Это не запутает странные имена файлов.
Обратите внимание, что сначала будет напечатано количество файлов, а затем имя каталога в отдельной строке. Если вы хотите сохранить формат OP, вам понадобится дополнительное форматирование, например
Если у вас есть определенный набор подкаталогов, которые вас интересуют, вы можете заменить их на
*
них.Почему это безопасно? (и поэтому достойный скрипта)
Имена файлов могут содержать любые символы, кроме
/
. Есть несколько символов, которые обрабатываются специально либо оболочкой, либо командами. К ним относятся пробелы, переводы строки и тире.Использование
for f in *
конструкции - это безопасный способ получить каждое имя файла, независимо от того, что оно содержит.Если у вас есть имя файла в переменной, вы все равно должны избегать таких вещей, как
find $f
. Если бы$f
содержалось имя файла-test
, выfind
бы пожаловались на вариант, который вы только что дали. Чтобы избежать этого, используйте./
перед именем; таким образом, он имеет то же значение, но больше не начинается с тире.Новые строки и пробелы также являются проблемой. Если
$f
в качестве имени файла содержится "привет, приятель"find ./$f
, естьfind ./hello, buddy
. Вы говорите,find
чтобы посмотреть./hello,
иbuddy
. Если таковых не существует, он будет жаловаться и никогда не заглядывает внутрь./hello, buddy
. Этого легко избежать - используйте кавычки вокруг ваших переменных.Наконец, имена файлов могут содержать новые строки, поэтому подсчет новых строк в списке имен файлов не будет работать; Вы получите дополнительный счет для каждого имени файла с новой строкой. Чтобы избежать этого, не считайте новые строки в списке файлов; вместо этого подсчитайте переводы строки (или любой другой символ), которые представляют один файл. Вот почему
find
команда просто-exec echo \;
и нет-exec echo {} \;
. Я хочу напечатать только одну новую строку для подсчета файлов.источник
-mindepth 1
-printf '\n'
вместо-exec echo
.-printf
, но не если вы хотите, чтобы она работала, например, на FreeBSD.Предполагая, что вы ищете стандартное решение Linux, сравнительно простой способ добиться этого заключается в
find
:Где
find
пересекает две указанные подкаталоги, в-maxdepth
1, что предотвращает дальнейшую рекурсию и сообщает только файлы (-type f
), разделенные символами новой строки. Затем результат передается дляwc
подсчета количества этих строк.источник
find . -maxdepth 1 -type d
выводом?find $dirs ...
или (б) если они находятся исключительно в одном каталоге более высокого уровня, глобус из этого каталога,find */ ...
-exec echo
к вашей команде поиска - таким образом, оно не отображает имя файла, просто перевод строки.Под «без рекурсии» вы имеете в виду, что если
directoryName1
есть подкаталоги, то вы не хотите считать файлы в подкаталогах? Если это так, вот способ подсчитать все обычные файлы в указанных каталогах:Обратите внимание, что
-f
тест выполняет две функции: он проверяет, является ли запись, совпадающая с одним из указанных выше глобусов, обычным файлом, и проверяет, была ли запись совпадающей (если один из глобусов ничего не соответствует, шаблон остается как есть). Если вы хотите сосчитать все записи в указанных каталогах независимо от их типа, замените-f
на-e
.В Ksh есть способ сделать шаблоны соответствующими точечным файлам и создать пустой список в случае, если ни один файл не соответствует шаблону. Таким образом, в ksh вы можете считать обычные файлы следующим образом:
или все файлы просто так:
У Bash есть разные способы сделать это проще. Для подсчета обычных файлов:
Для подсчета всех файлов:
Как обычно, это еще проще в Zsh. Для подсчета обычных файлов:
Перейдите
(DN.)
на(DN)
подсчет всех файлов.¹ Обратите внимание, что каждый шаблон соответствует самому себе, в противном случае результаты могут быть отключены (например, если вы считаете файлы, начинающиеся с цифры, вы не можете просто сделать это,
for x in [0-9]*; do if [ -f "$x" ]; then …
потому что может быть файл с именем[0-9]foo
).источник
Основываясь на сценарии подсчета , ответе Шона и трюке Bash, чтобы убедиться, что даже имена файлов с символами новой строки напечатаны в удобной форме в одну строку:
printf %q
заключается в печати цитируемой версии строки, то есть строки в одну строку, которую вы можете поместить в скрипт Bash, чтобы интерпретировать ее как буквенную строку, включающую (потенциально) переводы строки и другие специальные символы. Например, см.echo -n $'\tfoo\nbar'
Противprintf %q $'\tfoo\nbar'
.Команда
find
работает, просто печатая один символ для каждого файла, а затем подсчитывая их вместо подсчета строк.источник
Вот «грубая сила» -ish способ получить результат, используя
find
,echo
,ls
,wc
,xargs
иawk
.источник
источник
источник