Como usar wc e piping para encontrar quantos arquivos e diretórios estão em um determinado diretório?

10

Como posso usar a palavra counter ( wc) e a canalização para contar quantos arquivos ou diretórios estão no /usr/bindiretório?

dinheiro
fonte
Esta lição de casa? Não há problema em pedir ajuda, basta identificá-lo como tal, se for o caso.
slm
sim, mas eu posto aqui para ter uma idéia de como conseguir algo, já que sou novo no Linux e pode ser muito complicado. E eu já resolvi a pergunta acima com este comando
cash
ls / bin / usr / bin | classificar | uniq wc -
numerário
np. É perfeitamente bom pedir ajuda! Apenas identifique-o para que as pessoas saibam que todo mundo aqui geralmente fica feliz em ajudar as pessoas que estão tentando aprender os pontos mais delicados do Unix.
slm

Respostas:

13

Uma abordagem seria usar lspara nos fornecer uma lista dos arquivos, mas queremos que essa lista mostre apenas 1 arquivo ou diretório por linha. A -1opção fará isso por nós.

$ ls -1
dir1
dir2
dir3
fileA
fileB
fileC

Exemplo

Crie os dados de amostra acima em um diretório vazio.

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

Verifique-o:

$ ls
dir1  dir2  dir3  fileA  fileB  fileC

Agora, para contar, você pode usar wc -lpara contar o número de linhas, que correspondem a um arquivo ou diretório na ls -1saída.

$ ls -1 | wc -l
6

(observe, porém, que ele não inclui os arquivos ocultos)

Contando arquivos ou diretórios, apenas não juntos

Para contar arquivos ou diretórios, você precisa mudar um pouco sua tática. Nesse caso, eu usaria, ls -lpois mostra o que é um diretório e o que é um arquivo.

Exemplo

$ 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

Em seguida, podemos usar greppara filtrar diretórios ou não diretórios da seguinte forma:

# 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

Agora basta usar wc -lnovamente para contar o acima:

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

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

Porém, você pode evitar wccompletamente e usar grepa -copção:

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

(novamente, arquivos ocultos não estão incluídos. Observe que diretórios e regulares são dois tipos de arquivos. Existem muitos outros como pipes nomeados, links simbólicos, dispositivos, soquetes ...).

Recursão

Se você precisar encontrar os arquivos e diretórios de forma recursiva /usr/bin, provavelmente desejará alterar completamente as táticas e usar outra ferramenta chamada find.

Exemplo

$ find /usr/bin | wc -l
4632

(embora acima de /usr/binsi esteja incluído na contagem)

As mesmas técnicas que usei acima podem ser empregadas lspara fazer algo semelhante, mas lsgeralmente não é uma boa ferramenta para analisar a saída. findpor outro lado, foi construído para isso e oferece opções para localizar arquivos ou diretórios.

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

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

(observe que, desta vez, findestá incluindo arquivos ocultos (exceto .e ..)).

novas linhas?

Eu nunca descobri por que um caractere de nova linha é um caractere legal a ser usado ao criar nomes de arquivo ou diretório. Portanto, os métodos discutidos acima usam wce lsnão os contendem, portanto, use-os com isso em mente.

Exemplo

Crie um diretório e nome de arquivo com novas linhas.

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

ls mostra-os corretamente:

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

Mas wcconta os diretórios e arquivos que contêm novas linhas como 2 itens, não um.

$ ls -1 | wc -l
10

Um método para contornar isso, se usar a implementação GNU de, findé usar finda capacidade de imprimir outra coisa no lugar de cada arquivo que encontrar e depois contá-los.

Exemplo

$ find . -printf . | wc -c
9

Aqui estamos encontrando tudo no diretório atual (exceto ..) e imprimindo um ponto ( .) para cada um e, em seguida, contando os pontos usando wca capacidade de contar bytes em vez de linhas wc -c,.

Referências

slm
fonte
Embora os arquivos inseridos /usr/binsejam todos bem formatados (e também não contenham espaços, portanto, tecnicamente, você poderia apenas echo * | wc -w), é importante notar que tudo isso ocorrerá nos nomes de arquivos que contêm novas linhas.
evilsoup
@ evilsoup - não, eu não acredito que ls -lou ls -1quebre b / c, estamos contando linhas, não palavras! O findpode quebrar, mas, novamente, estamos contando linhas, não palavras.
slm
O que quero dizer é que isso (acho que estou no Windows agora, portanto não posso testar) será interrompido se os arquivos contiverem novas linhas . Portanto, touch $'foo\nbar'dentro de um diretório vazio seguido por um de seus comandos (digamos ls -1 | wc -l), serão relatados dois arquivos em vez de um - porque esse arquivo possui duas linhas no que diz wcrespeito. A menos que lssubstitua as novas linhas por algum outro personagem (acho que não, mas novamente não estou em posição de testar agora).
evilsoup
@evilsoup - char correto de nova linha. é um caractere legal. para nomes de arquivo, e os métodos não seriam capazes de lidar com esses tipos de nomes de arquivos corretamente.
slm
@StephaneChazelas - é wc -cum problema ao contar os períodos?
slm
5

Se você deseja obter uma divisão do número de cada tipo de arquivo recursivamente sob algum diretório, com o GNU find, você pode:

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 (&)/'

No /usr/binmeu sistema, isso fornece:

   3727 regular files
    710 symbolic links

Ativado /dev:

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

Para links simbólicos, se você preferir contá-los como o tipo de arquivo ao qual eles apontam, em vez disso symbolic links, altere-o para:

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 (&)/'

O que agora dá para o meu /usr/bin:

      1 directories
   4434 regular files
      2 broken symbolic links

(um link simbólico quebrado é um link simbólico para um arquivo para o qual findnão é possível determinar o tipo porque o arquivo não existe ou está em um diretório ao qual você não tem acesso ou há um loop na resolução do caminho do arquivo No meu caso, aqueles 2 onde links simbólicos para arquivos que agora se foram).

Nenhum deles conta .e ... Se você os quisesse incluídos (por que você desejaria?), Não há outra maneira findde assumir que eles estão lá para todos os diretórios e contá-los sistematicamente:

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 (&)/'

Que então dá no meu /usr/bin:

      2 directories
   3727 regular files
    710 symbolic links

Se você não tem acesso ao GNU find, pode reescrever o primeiro como:

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

Agora, estritamente falando, não contamos arquivos, mas entradas de diretório . Um diretório como /usr/binnormalmente possui várias entradas que apontam para o mesmo arquivo. Por exemplo, aqui, eu tenho:

$ 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

Essas são 3 entradas de diretório (também conhecidas como nomes de arquivos ou links físicos) para o mesmo arquivo (aquele com o inode 672252. Para contar arquivos em vez de entradas de diretório e com GNU finde GNU uniq(ignorando .e ..arquivos que de qualquer forma são links físicos para outros diretórios):

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 (&)/'

No meu /usr/bin, isso dá:

   3711 regular files
    710 symbolic links
Stéphane Chazelas
fonte
0

Você não disse se deseja que todo o arquivo seja em / usr / bin recursivamente ou logo abaixo do primeiro nível. Além disso, como você vai conseguir as palavras que está contando? A maneira usual de descobrir é executar a busca no wc. Assim: find / usr / bin | wc -l Find listará tudo o que há lá, diretórios e arquivos. Wc -l contará todas as linhas na saída de localização. Isso é um trabalho de classe? Está tudo bem se estiver, mas eu queria saber por que você precisava dessas informações para que eu pudesse adaptar a resposta com mais cuidado. Entre em contato se precisar de mais. Costa

cdr
fonte
0

No bash, sem ferramentas externas.

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

No bash, sem ferramentas externas e recursão.

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
fonte
Observe que o segundo seguiria links simbólicos ao recursar (e contaria links simbólicos para arquivos regulares como arquivos regulares e links simbólicos para dirs como dirs), não contaria arquivos e diretórios no diretório atual e não contaria .nem ..entradas. Você pode querer desambiguar arquivo x arquivo normal.
Stéphane Chazelas