¿Cómo usar wc y piping para encontrar cuántos archivos y directorios hay en un directorio determinado?

10

¿Cómo puedo usar word counter ( wc) y piping para contar cuántos archivos o directorios hay en el /usr/bindirectorio?

efectivo
fuente
Esta tarea? Está bien pedir ayuda, simplemente identifíquela como tal, si es así.
slm
Sí, pero publico aquí para tener una idea de cómo lograr algo, ya que soy nuevo en Linux y puede ser muy complicado. Y ya resuelvo la pregunta anterior con este comando
efectivo
ls / bin / usr / bin | ordenar | uniq | wc -
efectivo
notario público. ¡Está perfectamente bien pedir ayuda! Simplemente etiquételo para que la gente sepa que todos aquí están felices de ayudar a las personas que están tratando de aprender los puntos más delicados de Unix.
slm

Respuestas:

13

Un enfoque sería utilizar lspara darnos una lista de los archivos, pero queremos que se garantice que esta lista muestre solo 1 archivo o directorio por línea. El -1interruptor hará esto por nosotros.

$ ls -1
dir1
dir2
dir3
fileA
fileB
fileC

Ejemplo

Cree los datos de muestra anteriores en un directorio vacío.

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

Revisalo:

$ ls
dir1  dir2  dir3  fileA  fileB  fileC

Ahora para contar, puede usar wc -lpara contar el número de líneas, que corresponden a un archivo o directorio en la ls -1salida.

$ ls -1 | wc -l
6

(tenga en cuenta sin embargo que no incluye los archivos ocultos)

Contando archivos o directorios, simplemente no juntos

Para contar archivos o directorios, debe cambiar ligeramente su táctica. En este caso, lo usaría ls -lya que muestra qué es un directorio y qué es un archivo.

Ejemplo

$ 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

Luego podemos usar greppara filtrar directorios o no directorios de esta manera:

# 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

Ahora solo use wc -lnuevamente para contar lo anterior:

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

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

Sin embargo, puede evitar por wccompleto y usar grepla -copción:

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

(de nuevo, los archivos ocultos no están incluidos. Tenga en cuenta que los directorios y los archivos regulares son dos tipos de archivos. Hay muchos más como canalizaciones con nombre, enlaces simbólicos, dispositivos, enchufes ...).

Recursividad

Si necesita encontrar los archivos y directorios de forma recursiva, /usr/binentonces es probable que desee cambiar las tácticas por completo y utilizar otra herramienta llamada find.

Ejemplo

$ find /usr/bin | wc -l
4632

(aunque arriba /usr/binestá incluido en el recuento)

Las mismas técnicas que utilicé anteriormente podrían emplearse lspara hacer algo similar, pero lsgeneralmente no es una buena herramienta para analizar la salida. findPor otro lado, se creó para esto y ofrece conmutadores para buscar archivos o directorios.

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

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

(tenga en cuenta que esta vez, findincluye archivos ocultos (excepto .y ..)).

nuevas líneas?

Nunca he descubierto por qué un carácter de nueva línea es un carácter legal para usar al crear nombres de archivo o nombres de directorio. Entonces, los métodos discutidos anteriormente usan wcy lsno competirían con estos, así que úselos con eso en mente.

Ejemplo

Cree un directorio y nombre de archivo con nuevas líneas.

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

ls los muestra correctamente:

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

Pero wccuenta los directorios y archivos que contienen nuevas líneas como 2 elementos, no uno.

$ ls -1 | wc -l
10

Un método para evitar esto, si utiliza la implementación de GNU findes utilizar findla capacidad de imprimir algo más en lugar de cada archivo que encuentra y luego contarlos.

Ejemplo

$ find . -printf . | wc -c
9

Aquí estamos encontrando todo en el directorio actual (excepto ..), y la impresión de un punto ( .) para cada uno, y luego contar los puntos que utilizan wc's capacidad de contar bytes en lugar de líneas, wc -c.

Referencias

slm
fuente
Si bien todos los archivos /usr/binestarán bien formateados (y tampoco contendrán espacios, por lo que técnicamente podría simplemente echo * | wc -w), vale la pena señalar que todos estos se romperán en los nombres de archivo que contienen nuevas líneas.
evilsoup
@evilsoup: ¡no, no creo ls -lo ls -1romperé b / c, estamos contando líneas, no palabras! El findpuede romper, pero de nuevo, estamos contando líneas no palabras.
slm
Lo que quiero decir es que esto (creo, estoy en Windows ahora, así que no puedo probar) se romperá si los archivos contienen nuevas líneas . Por lo tanto, touch $'foo\nbar'dentro de un directorio vacío seguido de uno de sus comandos (digamos ls -1 | wc -l) informará dos archivos en lugar de uno, porque ese archivo tiene dos líneas en lo que wcrespecta. A menos que lsreemplace las líneas nuevas con algún otro personaje (no creo que lo haga, pero nuevamente no estoy en condiciones de probarlo en este momento).
evilsoup
@evilsoup - correcto, nueva línea char. es un char legal. para los nombres de archivo, y los métodos no podrían competir con esos tipos de nombres de archivo correctamente.
slm
@StephaneChazelas: ¿es wc -cun problema al contar los períodos?
slm
5

Si desea obtener un desglose del número de cada tipo de archivo de forma recursiva en algún directorio, con GNU find, puede hacer:

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

En /usr/binmi sistema, eso da:

   3727 regular files
    710 symbolic links

En /dev:

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

Para los enlaces simbólicos, si prefiere contarlos como el tipo de archivo al que apuntan en lugar de hacerlo symbolic links, puede cambiarlo a:

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

Lo que ahora da por mi /usr/bin:

      1 directories
   4434 regular files
      2 broken symbolic links

(un enlace simbólico roto es un enlace simbólico a un archivo para el que findno se puede determinar el tipo, ya sea porque el archivo no existe, o está en un directorio al que no tiene acceso o hay un bucle en la resolución de la ruta del archivo En mi caso, esos 2 eran enlaces simbólicos a archivos que ahora se han ido).

Ninguno de esos cuenta .y ... Si quisieras incluirlos (¿por qué lo harías?), No hay otra forma findde asumir que están allí para cada directorio y contarlos sistemáticamente:

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 luego da en mi /usr/bin:

      2 directories
   3727 regular files
    710 symbolic links

Si no tiene acceso a GNU find, puede reescribir el primero 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

Ahora, estrictamente hablando, no hemos estado contando archivos sino entradas de directorio . Un directorio como /usr/binsuele tener varias entradas que apuntan al mismo archivo. Por ejemplo, aquí tengo:

$ 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

Esas son 3 entradas de directorio (también conocidas como nombres de archivos también conocidos como enlaces duros) al mismo archivo (el que tiene el inodo 672252. Para contar archivos en lugar de entradas de directorio y con GNU findy GNU uniq(ignorando .y ..archivos que de todos modos son enlaces duros a otros directorios):

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

En mi /usr/bin, eso da:

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

No ha dicho si quiere todo el archivo en / usr / bin de forma recursiva o solo en el primer nivel. Además, ¿cómo vas a obtener las palabras que estás contando? La forma habitual de averiguarlo es ejecutar find en wc. De esta manera: find / usr / bin | wc -l Find enumerará todo lo que hay allí, directorios y archivos. Wc -l contará todas las líneas en la salida de búsqueda. ¿Es esta una tarea de clase? Está bien si es así, pero me preguntaba por qué necesitabas esta información para poder adaptar la respuesta con más cuidado. Avísame si necesitas más. Costa

cdr
fuente
0

En bash, sin herramientas externas.

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

En bash, sin herramientas externas y recursividad.

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
fuente
Tenga en cuenta que el segundo seguiría los enlaces simbólicos cuando se repite (y contará los enlaces simbólicos a los archivos normales como archivos normales, y los enlaces simbólicos a los directorios como directorios), no contará los archivos y directorios en el directorio actual y no contará .ni las ..entradas. Es posible que desee desagregar el archivo frente al archivo normal.
Stéphane Chazelas