Мне нужно проверить все подкаталоги и сообщить, сколько файлов (без дальнейшей рекурсии) они содержат:
directoryName1 numberOfFiles
directoryName2 numberOfFiles
Мне нужно проверить все подкаталоги и сообщить, сколько файлов (без дальнейшей рекурсии) они содержат:
directoryName1 numberOfFiles
directoryName2 numberOfFiles
Ответы:
Это делает это безопасным и портативным способом. Это не запутает странные имена файлов.
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
-printf '\n'
вместо -exec echo
.
-printf
, но не если вы хотите, чтобы она работала, например, на FreeBSD.
Предполагая, что вы ищете стандартное решение Linux, сравнительно простой способ добиться этого заключается в find
:
find dir1/ dir2/ -maxdepth 1 -type f | wc -l
Где find
пересекает две указанные подкаталоги, в -maxdepth
1, что предотвращает дальнейшую рекурсию и сообщает только файлы ( -type f
), разделенные символами новой строки. Затем результат передается для wc
подсчета количества этих строк.
find . -maxdepth 1 -type d
выводом?
find $dirs ...
или (б) если они находятся исключительно в одном каталоге более высокого уровня, глобус из этого каталога,find */ ...
-exec echo
к вашей команде поиска - таким образом, оно не отображает имя файла, просто перевод строки.
Под «без рекурсии» вы имеете в виду, что если 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
или все файлы просто так:
FIGNORE='.?(.)'
files=(~(N)directoryName1/* ~(N)directoryName2/*)
count=${#files}
У Bash есть разные способы сделать это проще. Для подсчета обычных файлов:
shopt -s dotglob nullglob
count=0
for x in directoryName1/* directoryName2/*; do
if [ -f "$x" ]; then ((++count)); fi
done
Для подсчета всех файлов:
shopt -s dotglob nullglob
files=(directoryName1/* directoryName2/*)
count=${#files}
Как обычно, это еще проще в Zsh. Для подсчета обычных файлов:
files=({directoryName1,directoryName2}/*(DN.))
count=$#files
Перейдите (DN.)
на (DN)
подсчет всех файлов.
¹ Обратите внимание, что каждый шаблон соответствует самому себе, в противном случае результаты могут быть отключены (например, если вы считаете файлы, начинающиеся с цифры, вы не можете просто сделать это, 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
работает, просто печатая один символ для каждого файла, а затем подсчитывая их вместо подсчета строк.
Вот «грубая сила» -ish способ получить результат, используя find
, echo
, ls
, wc
, xargs
и awk
.
find . -maxdepth 1 -type d -exec sh -c "echo '{}'; ls -1 '{}' | wc -l" \; | xargs -n 2 | awk '{print $1" "$2}'
for i in *; do echo $i; ls $i | wc -l; done
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)
: для всех каталогов подсчитайте количество записей в этом каталоге (включая файлы со скрытыми точками, исключая.
и..
)