Как сообщить о количестве файлов во всех подкаталогах?

Для восстановления кэша исполняемых команд использовать rehash или hash -rf.

Удостоверьтесь, что Вы не сбросили hash_list_all опция (это вызывает даже меньше доступов к диску, но делает обновление кэша менее часто).

Если Вы не хотите должными быть вводить команду, можно сказать zsh не доверять своему кэшу при завершении, вставив следующую строку Ваш ~/.zshrc¹:

zstyle ":completion:*:commands" rehash 1

Существует стоимость производительности, но это незначительно на типичном рабочем столе, устанавливающем сегодня. (Это не, если Вы имеете $PATH на NFS или исчерпавшей ресурсы RAM системе.)

zstyle сама команда документируется в zshmodule страница справочника. Значения стилей документируются в zshcompsys и zshcompwid страницы справочника, или можно считать источник (здесь, _command_names функция). Если Вы хотели некоторую читаемую документацию … при нахождении некоторых сообщите мне!

¹ требует zsh≥4.3.3, Chris Johnsen спасибо

24
23.10.2011, 07:47
7 ответов

Это делает это безопасным и портативным способом. Это не запутается странными именами файлов.

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 {} \;. Я только хочу распечатать единственную новую строку в целях соответствия файлам.

30
27.01.2020, 19:41
  • 1
    Почему существует человек в мире, который использует новые строки в имени файла? Спасибо за ответ. –  ShyBoy 23.10.2011, 22:04
  • 2
    могут содержать любые символы кроме / и нулевой символ, я верю. dwheeler.com/essays/fixing-unix-linux-filenames.html –  Flimm 28.10.2011, 02:22
  • 3
    Количество будет включать сам каталог. Если Вы хотите исключить это из количества, использовать -mindepth 1 –  toxalot 10.03.2014, 06:20
  • 4
    Вы могли также использовать -printf '\n' вместо -exec echo. –  toxalot 10.03.2014, 13:06
  • 5
    @toxalot Вы можете, если у Вас есть находка, которая поддерживает -printf, но не, если Вы хотите, чтобы это работало над FreeBSD, например. –  Shawn J. Goff 10.03.2014, 14:07

Предположение, что Вы ищете стандартное решение Linux, относительно простой способ достигнуть этого, с find:

find dir1/ dir2/ -maxdepth 1 -type f | wc -l

Где find пересекает два указанных подкаталога, к a -maxdepth из 1, который предотвращает дальнейшую рекурсию и только сообщает о файлах (-type f) разделенный новыми строками. Результат затем передается по каналу к wc считать количество тех строк.

6
27.01.2020, 19:41
  • 1
    у меня есть больше чем 2 директора, как я могу объединить Вашу команду с find . -maxdepth 1 -type d вывод? –  ShyBoy 23.10.2011, 09:14
  • 2
    Вы могли любой (a) включать необходимые каталоги в переменную и find $dirs ... или, (b), если они находятся исключительно в одном высокоуровневом каталоге, шарике из того каталога, find */ ... –  jasonwryan 23.10.2011, 10:29
  • 3
    Это сообщит о неправильных результатах, если какое-либо имя файла будет иметь символ новой строки в нем. –  Shawn J. Goff 23.10.2011, 14:23
  • 4
    @Shawn:спасибо. я думал, что имел имена файлов с покрытыми пробелами, но не рассмотрел новых строк: какие-либо предложения для фиксации? –  jasonwryan 23.10.2011, 14:37
  • 5
    Добавить -exec echo к Вашей команде находки - тот путь это не повторяет имя файла, просто новая строка. –  Shawn J. Goff 23.10.2011, 14:43

“Без рекурсии”, сделайте Вы имеете в виду это если 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).

5
27.01.2020, 19:41

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

2
27.01.2020, 19:41

Вот "в лоб" - выход способ получить Ваш результат, с помощью 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}'
1
27.01.2020, 19:41
  • 1
    Эта работа. Но вывод смешал, если у Вас есть директора, которые имеют '' пространство на имя. –  ShyBoy 23.10.2011, 14:20
  • 2
    Это сообщит о неправильных результатах, если какое-либо имя файла будет иметь символ новой строки в нем. –  Shawn J. Goff 23.10.2011, 14:23
for i in *; do echo $i; ls $i | wc -l; done
-1
27.01.2020, 19:41
    for i in `ls -1`; do echo $i : `ls -1 $i|wc -l`; done
-1
27.01.2020, 19:41

Теги

Похожие вопросы