Как использовать wc и piping, чтобы узнать, сколько файлов и каталогов находится в определенном каталоге?

10

Как я могу использовать счетчик слов ( wc) и трубопровод, чтобы подсчитать, сколько файлов или каталогов в /usr/binкаталоге?

денежные средства
источник
Это домашнее задание ?? Можно попросить о помощи, просто определите ее как таковую, если это так.
slm
да, но я пишу здесь, чтобы получить представление о том, как чего-то добиться, так как я новичок в Linux, и это может быть очень сложно. И я уже решаю вопрос выше с этой командой
наличными
ls / bin / usr / bin | сортировать | uniq | wc -
наличными
нп. Это прекрасно, чтобы попросить о помощи! Просто пометьте его так, чтобы люди знали, что все здесь, как правило, рады помочь людям, которые пытаются изучить тонкости Unix.
SLM

Ответы:

13

Один из подходов состоит в том, чтобы использовать lsдля предоставления нам список файлов, но мы хотим, чтобы этот список гарантированно отображал только 1 файл или каталог на строку. -1Выключатель будет делать это для нас.

$ ls -1
dir1
dir2
dir3
fileA
fileB
fileC

пример

Создайте приведенные выше примеры данных в пустом каталоге.

$ mkdir dir{1..3}
$ touch file{A..C}

Проверь это:

$ ls
dir1  dir2  dir3  fileA  fileB  fileC

Теперь для подсчета вы можете использовать wc -lдля подсчета количества строк, которые соответствуют файлу или каталогу в ls -1выводе.

$ ls -1 | wc -l
6

(обратите внимание, однако, что он не включает скрытые файлы)

Подсчет файлов или каталогов, только не вместе

Для подсчета файлов или каталогов вам нужно немного изменить свою тактику. В этом случае я бы использовал, ls -lпоскольку он показывает, что такое каталог и что такое файл.

пример

$ ls -l
total 12
drwxrwxr-x 2 saml saml 4096 Nov 16 09:48 dir1
drwxrwxr-x 2 saml saml 4096 Nov 16 09:48 dir2
drwxrwxr-x 2 saml saml 4096 Nov 16 09:48 dir3
-rw-rw-r-- 1 saml saml    0 Nov 16 09:49 fileA
-rw-rw-r-- 1 saml saml    0 Nov 16 09:49 fileB
-rw-rw-r-- 1 saml saml    0 Nov 16 09:49 fileC

Затем мы можем использовать grepдля фильтрации каталогов или не-каталогов следующим образом:

# directories
$ ls -l | grep "^d"
drwxrwxr-x 2 saml saml 4096 Nov 16 09:48 dir1
drwxrwxr-x 2 saml saml 4096 Nov 16 09:48 dir2
drwxrwxr-x 2 saml saml 4096 Nov 16 09:48 dir3

# regular files
$ ls -l | grep "^-"
-rw-rw-r-- 1 saml saml    0 Nov 16 09:49 fileA
-rw-rw-r-- 1 saml saml    0 Nov 16 09:49 fileB
-rw-rw-r-- 1 saml saml    0 Nov 16 09:49 fileC

Теперь просто используйте еще wc -lраз для подсчета выше:

# directories
$ ls -l | grep "^d" | wc -l
3

# regular files
$ ls -l | grep "^-" | wc -l
3

Хотя, вы можете избежать в wcцелом, и использования grep«s -cварианта:

$ ls -l | grep -c '^d'

(опять же, скрытые файлы не включены. Обратите внимание, что каталоги и обычные файлы - это два типа файлов. Есть много других, таких как именованные каналы, символические ссылки, устройства, сокеты ...).

Рекурсия

Если вам нужно рекурсивно находить файлы и каталоги, /usr/binто вы, вероятно, захотите полностью изменить тактику и использовать другой инструмент под названием find.

пример

$ find /usr/bin | wc -l
4632

(хотя над /usr/binсобой входит в счет)

Те же методы, которые я использовал выше, могут быть использованы lsдля lsсоздания чего-то похожего, но, как правило, не являются хорошим инструментом для анализа выходных данных. findс другой стороны, был создан для этого и предлагает переключатели для поиска файлов или каталогов.

# find files
$ find /usr/bin -type f

# find directories
$ find /usr/bin -type d

(обратите внимание, что в этот раз findвключаются скрытые файлы (кроме .и ..)).

строки?

Я никогда не понимал, почему символ новой строки является допустимым символом, который следует использовать при создании имен файлов или каталогов. Таким образом, методы, рассмотренные выше, используют wcи lsне будут противоречить этим, поэтому используйте их с учетом этого.

пример

Создайте каталог и имя файла с символами новой строки.

$ mkdir $'dir4\n5'
$ touch $'fileD\nE'

ls показывает их правильно:

$ ls -1
dir1
dir2
dir3
dir4?5
fileA
fileB
fileC
fileD?E

Но wcсчитает каталоги и файлы, которые содержат переводы строк, как 2 элемента, а не как один.

$ ls -1 | wc -l
10

Один из способов обойти это, если используется реализация GNU find- это использовать findвозможность печатать что-то еще вместо каждого файла, который он находит, а затем подсчитывать их.

пример

$ find . -printf . | wc -c
9

Здесь мы находим все в текущем каталоге ( за исключением ..), и печать точка ( .) для каждого, а затем подсчет точек , используя wcспособность «s для подсчета байт вместо строк, wc -c.

Ссылки

SLM
источник
Хотя все файлы /usr/binбудут хорошо отформатированы (и также не будут содержать пробелов, так что технически вы могли бы даже просто echo * | wc -w), стоит отметить, что все они будут ломаться на именах файлов, содержащих символы новой строки.
evilsoup
@evilsoup - нет, я не верю, ls -lили ls -1мы сломаемся, потому что мы считаем строки, а не слова! findМожет сломаться, но опять же , мы рассчитываем строки не слово.
SLM
Я имею в виду, что это (я думаю, я сейчас на Windows, поэтому я не могу проверить) сломается, если файлы содержат символы новой строки . Таким образом, touch $'foo\nbar'в пустом каталоге, за которым следует одна из ваших команд (скажем ls -1 | wc -l), будет отчет о двух файлах, а не об одном - потому что этот файл состоит из двух строк wc. Если не lsзаменить символы новой строки каким-либо другим символом (я не думаю, что это так, но опять же я не в состоянии проверить прямо сейчас).
evilsoup
@evilsoup - правильный символ перевода строки. это законный символ для имен файлов, и методы не смогут правильно бороться с этими типами имен файлов.
SLM
@StephaneChazelas - wc -cэто проблема при подсчете периодов?
SLM
5

Если вы хотите получить рекурсивную разбивку числа файлов каждого типа под некоторым каталогом, с помощью GNU find, вы можете сделать:

find /some/dir/. ! -name . -printf '%y\n' | sort | uniq -c | sed '
  s/f/regular files/;t
  s/d/directories/;t
  s/l/symbolic links/;t
  s/s/Unix domain sockets/;t
  s/b/block devices/;t
  s/c/character devices/;t
  s/p/FIFOs/;t
  s/D/Doors/;t
  s/n/network special files/;t
  s/.$/others (&)/'

В /usr/binмоей системе это дает:

   3727 regular files
    710 symbolic links

На /dev:

     83 block devices
    203 character devices
     31 directories
    426 symbolic links
      1 FIFOs
      1 Unix domain sockets

Для символических ссылок, если вы предпочитаете считать их типом файла, на который они указывают, а не его symbolic links, вы можете изменить его на:

find /some/dir/. ! -name . -printf '%Y\n' | sort | uniq -c | sed '
  s/f/regular files/;t
  s/d/directories/;t
  s/N/broken symbolic links/;t
  s/s/Unix domain sockets/;t
  s/b/block devices/;t
  s/c/character devices/;t
  s/p/FIFOs/;t
  s/D/Doors/;t
  s/n/network special files/;t
  s/.$/others (&)/'

Который сейчас дает для моего /usr/bin:

      1 directories
   4434 regular files
      2 broken symbolic links

(неработающая символическая ссылка - это символическая ссылка на файл, для которой findневозможно определить тип, либо потому, что файл не существует, либо находится в каталоге, к которому у вас нет доступа, либо в разрешении пути к файлу есть цикл В моем случае те 2, где символические ссылки на файлы, которые сейчас ушли).

Никто из тех не считается .и ... Если вы хотите, чтобы они были включены (почему вы?), Нет другого пути, findкроме как предположить, что они есть для каждого каталога и подсчитывать их систематически:

find /some/dir/. -printf '%y\n' \( -name . -printf 'd\n' -o \
  -type d -printf 'd\nd\n' \)  | sort | uniq -c | sed '
  s/f/regular files/;t
  s/d/directories/;t
  s/l/symbolic links/;t
  s/s/Unix domain sockets/;t
  s/b/block devices/;t
  s/c/character devices/;t
  s/p/FIFOs/;t
  s/D/Doors/;t
  s/n/network special files/;t
  s/.$/others (&)/'

Который затем дает на моем /usr/bin:

      2 directories
   3727 regular files
    710 symbolic links

Если у вас нет доступа к GNU find, вы можете переписать первый как:

find /some/dir/. ! -name . \( \
  -type f -exec printf '%.0sregular files\n' {} + -o \
  -type d -exec printf '%.0sdirectories\n' {} + -o \
  -type l -exec printf '%.0ssymbolic links\n' {} + -o \
  -type s -exec printf '%.0sUnix domain sockets\n' {} + -o \
  -type b -exec printf '%.0sblock devices\n' {} + -o \
  -type c -exec printf '%.0scharacter devices\n' {} + -o \
  -type p -exec printf '%.0sFIFOs\n' {} + -o \
  -exec printf '%.0sothers\n' {} + \) | sort | uniq -c

Теперь, строго говоря, мы не считали файлы, а только записи в каталогах . Каталог, как /usr/binправило, имеет несколько записей, которые указывают на один и тот же файл. Например, здесь у меня есть:

$ ls -lid /usr/bin/{nvi,nview,nex}
672252 -rwxr-xr-x 3 root root 434616 May 25 07:40 /usr/bin/nex
672252 -rwxr-xr-x 3 root root 434616 May 25 07:40 /usr/bin/nvi
672252 -rwxr-xr-x 3 root root 434616 May 25 07:40 /usr/bin/nview

Это 3 записи каталога (или имена файлов или жесткие ссылки) к одному и тому же файлу (с индексом 672252. Для подсчета файлов вместо записей каталога и с помощью GNU findи GNU uniq(игнорирование .и ..файлы, которые в любом случае являются жесткими ссылками на другие каталоги):

find /some/dir/. ! -name . -printf '%y\t%D:%i\n' |
  sort -u |
  cut -f1 |
  uniq -c |
  sed '
    s/f/regular files/;t
    s/d/directories/;t
    s/l/symbolic links/;t
    s/s/Unix domain sockets/;t
    s/b/block devices/;t
    s/c/character devices/;t
    s/p/FIFOs/;t
    s/d/Doors/;t
    s/n/network special files/;t
    s/.$/others (&)/'

По моему /usr/bin, это дает:

   3711 regular files
    710 symbolic links
Стефан Шазелас
источник
0

Вы не сказали, хотите ли вы, чтобы все файлы находились в / usr / bin рекурсивно или просто под первым уровнем. Кроме того, как вы собираетесь получить слова, которые вы рассчитываете? Обычный способ узнать это запустить find в wc. Как это: найти / usr / bin | wc -l Find перечислит там все, каталоги и файлы. Wc -l подсчитает все строки в выводе find. Это классное задание? Это нормально, если это так, но мне было интересно, зачем вам нужна эта информация, чтобы я мог более тщательно адаптировать ответ. Пожалуйста, дайте мне знать, если вам нужно больше. ребро

корд
источник
0

В баш, без внешних инструментов.

cd dir/ || exit; shopt -s nullglob; shopt -s dotglob; count=(*); echo "${#count}"

В bash, без внешних инструментов и рекурсии.

shopt -s globstar; shopt -s dotglob 
for dir in **/*/; do 
  unset d f
  for files in "$dir"*; do 
    [[ -f $files ]] && ((++f))
    [[ -d $files ]] && ((++d))
  done; 
  printf '%s\n' "$dir -  files: ${f:-0} - directories: ${d:-0}"
done
llua
источник
Обратите внимание, что вторая будет следовать символическим ссылкам при повторном обращении (и считать символические ссылки на обычные файлы как обычные файлы, а символические ссылки на директории как директории), не будет подсчитывать файлы и каталоги в текущем каталоге и не будет подсчитывать .ни ..записи, ни записи. Вы можете хотеть избавиться от неоднозначности файла против обычного файла.
Стефан Шазелас