¿Por qué quieres usar findcuándo lo hará Bash? (shopt -s dotglob; for dir in */; do all=("$dir"/*); echo "$dir: ${#all[@]}"; done): Para todos los directorios, cuente el número de entradas en ese directorio (incluidos los archivos ocultos de punto, con exclusión .e ..)
janmoesen
@janmoesen ¿Por qué no respondiste eso? Soy nuevo en las secuencias de comandos de shell, pero no puedo ver ningún problema con su método. Para mí, parece la mejor manera. Nadie ha votado tu comentario, pero tampoco ha comentado por qué podría ser malo. Las respuestas votadas tienen mucha más reputación que tú, por lo que me pregunto si me estoy perdiendo algo.
toxalot
@toxalot: no me molesté en agregarlo como respuesta porque era muy corto (y posiblemente un tono ligeramente condescendiente). Siéntase libre de votar el comentario. :-) Además, la pregunta es algo vaga con respecto a qué significa "cuántos archivos". Mi solución cuenta los archivos y directorios "normales" ; tal vez el cartel realmente significa "archivos, no directorios". Otra cosa a tener en cuenta es que este bloqueo no tiene en cuenta los archivos de puntos "ocultos". Sin embargo, hay maneras de evitar esas dos trampas. Pero de nuevo: no estoy seguro de los requisitos exactos del póster original.
Janmoesen
Respuestas:
30
Esto lo hace de una manera segura y portátil. No se confundirá con nombres de archivo extraños.
for f in *; do [ -d ./"$f" ] && find ./"$f" -maxdepth 1 -exec echo \; | wc -l && echo $f; done
Tenga en cuenta que imprimirá primero el número de archivos, luego el nombre del directorio en una línea separada. Si desea mantener el formato de OP, necesitará más formatos, p. Ej.
for f in *; do [ -d ./"$f" ] && find ./"$f" -maxdepth 1 -exec echo \;|wc -l|tr '\n' ' ' && echo $f; done|awk '{print $2"\t"$1}'
Si tiene un conjunto específico de subdirectorios que le interesan, puede reemplazarlos *por ellos.
¿Por qué es esto seguro? (y por lo tanto digno de script)
Los nombres de archivo pueden contener cualquier carácter excepto /. Hay algunos caracteres que son tratados especialmente por el shell o por los comandos. Estos incluyen espacios, líneas nuevas y guiones.
Usar la for f in *construcción es una forma segura de obtener cada nombre de archivo, sin importar lo que contenga.
Una vez que tenga el nombre de archivo en una variable, aún debe evitar cosas como find $f. Si $fcontuviera el nombre del archivo -test, findse quejaría de la opción que acaba de darle. La forma de evitar eso es mediante el uso ./delante del nombre; de esta manera tiene el mismo significado, pero ya no comienza con un guión.
Las nuevas líneas y los espacios también son un problema. Si $fcontiene "hola, amigo" como nombre de archivo,, find ./$fes find ./hello, buddy. Estás diciendo findque mires ./hello,y buddy. Si no existen, se quejará y nunca se verá ./hello, buddy. Esto es fácil de evitar: use comillas alrededor de sus variables.
Finalmente, los nombres de archivo pueden contener nuevas líneas, por lo que contar líneas nuevas en una lista de nombres de archivos no funcionará; obtendrá un recuento adicional para cada nombre de archivo con una nueva línea. Para evitar esto, no cuente las nuevas líneas en una lista de archivos; en su lugar, cuente las nuevas líneas (o cualquier otro carácter) que represente un solo archivo. Es por eso que el findcomando tiene simplemente -exec echo \;y no -exec echo {} \;. Solo quiero imprimir una sola línea nueva con el fin de contar los archivos.
El recuento incluirá el directorio en sí. Si desea excluir eso del recuento, use-mindepth 1
toxalot
También puedes usar en -printf '\n'lugar de -exec echo.
toxalot
1
@toxalot puede hacerlo si tiene un hallazgo que admite -printf, pero no si desea que funcione en FreeBSD, por ejemplo.
Shawn J. Goff
6
Suponiendo que está buscando una solución estándar de Linux, una forma relativamente sencilla de lograrlo es con find:
find dir1/ dir2/ -maxdepth 1 -type f | wc -l
Donde findatraviesa los dos subdirectorios especificados, a uno -maxdepthde 1, lo que evita una mayor recursión y solo informa archivos ( -type f) separados por nuevas líneas. El resultado se canaliza wcpara contar el número de esas líneas.
Tengo más de 2 directorios ... ¿Cómo puedo combinar su comando con la find . -maxdepth 1 -type dsalida?
ShyBoy
Puede (a) incluir los directorios requeridos en una variable y find $dirs ...(b) si están exclusivamente en el directorio de un nivel superior, glob desde ese directorio,find */ ...
jasonwryan
1
Esto informará resultados incorrectos si algún nombre de archivo tiene un carácter de nueva línea.
Shawn J. Goff el
@ Shawn: gracias. Pensé que tenía nombres de archivo con espacios cubiertos, pero no había considerado nuevas líneas: ¿alguna sugerencia para una solución?
jasonwryan
Agregue -exec echoa su comando de búsqueda, de esa manera no se hace eco del nombre del archivo, solo una nueva línea.
Shawn J. Goff el
5
Por "sin recursividad", ¿quiere decir que si directoryName1tiene subdirectorios, entonces no desea contar los archivos en los subdirectorios? Si es así, aquí hay una manera de contar todos los archivos regulares en los directorios indicados:
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
Tenga en cuenta que la -fprueba realiza dos funciones: comprueba si la entrada que coincide con uno de los globos de más arriba es un archivo normal y prueba si la entrada era una coincidencia (si uno de los globos no coincide, el patrón permanece como está). Si desea contar todas las entradas en los directorios dados independientemente de su tipo, reemplace -fcon -e.
Ksh tiene una manera de hacer que los patrones coincidan con los archivos de puntos y producir una lista vacía en caso de que ningún archivo coincida con un patrón. Entonces, en ksh puedes contar archivos regulares como este:
FIGNORE='.?(.)'
count=0
for x in ~(N)directoryName1/* ~(N)directoryName2/*; do
if [ -f "$x" ]; then ((++count)); fi
done
¹ Tenga en cuenta que cada patrón coincide, de lo contrario, los resultados podrían estar apagados (por ejemplo, si está contando archivos que comienzan con un dígito, no puede hacerlo simplemente for x in [0-9]*; do if [ -f "$x" ]; then …porque puede haber un archivo llamado [0-9]foo).
Basado en un guión de conteo , la respuesta de Shawn y un truco de Bash para asegurarse de que incluso los nombres de archivo con líneas nuevas se impriman en forma utilizable en una sola línea:
for f in *
do
if [ -d "./$f" ]
then
printf %q "$f"
printf %s ' '
find "$f" -maxdepth 1 -printf x | wc -c
fi
done
printf %qes imprimir una versión citada de una cadena, es decir, una cadena de una sola línea que podría poner en una secuencia de comandos Bash para interpretarla como una cadena literal que incluye (potencialmente) líneas nuevas y otros caracteres especiales. Por ejemplo, véase echo -n $'\tfoo\nbar'vs printf %q $'\tfoo\nbar'.
El findcomando funciona simplemente imprimiendo un solo carácter para cada archivo y luego contando esos en lugar de contar líneas.
Bienvenido a U&L. Las respuestas deben ser largas con explicaciones y no simplemente caídas de código. Expanda esto y explique lo que está sucediendo. Además, esta es una forma muy ineficiente de hacer esto y no maneja archivos con espacios, por ejemplo.
slm
-1
for i in `ls -1`; do echo $i : `ls -1 $i|wc -l`; done
find
cuándo lo hará Bash?(shopt -s dotglob; for dir in */; do all=("$dir"/*); echo "$dir: ${#all[@]}"; done)
: Para todos los directorios, cuente el número de entradas en ese directorio (incluidos los archivos ocultos de punto, con exclusión.
e..
)Respuestas:
Esto lo hace de una manera segura y portátil. No se confundirá con nombres de archivo extraños.
Tenga en cuenta que imprimirá primero el número de archivos, luego el nombre del directorio en una línea separada. Si desea mantener el formato de OP, necesitará más formatos, p. Ej.
Si tiene un conjunto específico de subdirectorios que le interesan, puede reemplazarlos
*
por ellos.¿Por qué es esto seguro? (y por lo tanto digno de script)
Los nombres de archivo pueden contener cualquier carácter excepto
/
. Hay algunos caracteres que son tratados especialmente por el shell o por los comandos. Estos incluyen espacios, líneas nuevas y guiones.Usar la
for f in *
construcción es una forma segura de obtener cada nombre de archivo, sin importar lo que contenga.Una vez que tenga el nombre de archivo en una variable, aún debe evitar cosas como
find $f
. Si$f
contuviera el nombre del archivo-test
,find
se quejaría de la opción que acaba de darle. La forma de evitar eso es mediante el uso./
delante del nombre; de esta manera tiene el mismo significado, pero ya no comienza con un guión.Las nuevas líneas y los espacios también son un problema. Si
$f
contiene "hola, amigo" como nombre de archivo,,find ./$f
esfind ./hello, buddy
. Estás diciendofind
que mires./hello,
ybuddy
. Si no existen, se quejará y nunca se verá./hello, buddy
. Esto es fácil de evitar: use comillas alrededor de sus variables.Finalmente, los nombres de archivo pueden contener nuevas líneas, por lo que contar líneas nuevas en una lista de nombres de archivos no funcionará; obtendrá un recuento adicional para cada nombre de archivo con una nueva línea. Para evitar esto, no cuente las nuevas líneas en una lista de archivos; en su lugar, cuente las nuevas líneas (o cualquier otro carácter) que represente un solo archivo. Es por eso que el
find
comando tiene simplemente-exec echo \;
y no-exec echo {} \;
. Solo quiero imprimir una sola línea nueva con el fin de contar los archivos.fuente
-mindepth 1
-printf '\n'
lugar de-exec echo
.-printf
, pero no si desea que funcione en FreeBSD, por ejemplo.Suponiendo que está buscando una solución estándar de Linux, una forma relativamente sencilla de lograrlo es con
find
:Donde
find
atraviesa los dos subdirectorios especificados, a uno-maxdepth
de 1, lo que evita una mayor recursión y solo informa archivos (-type f
) separados por nuevas líneas. El resultado se canalizawc
para contar el número de esas líneas.fuente
find . -maxdepth 1 -type d
salida?find $dirs ...
(b) si están exclusivamente en el directorio de un nivel superior, glob desde ese directorio,find */ ...
-exec echo
a su comando de búsqueda, de esa manera no se hace eco del nombre del archivo, solo una nueva línea.Por "sin recursividad", ¿quiere decir que si
directoryName1
tiene subdirectorios, entonces no desea contar los archivos en los subdirectorios? Si es así, aquí hay una manera de contar todos los archivos regulares en los directorios indicados:Tenga en cuenta que la
-f
prueba realiza dos funciones: comprueba si la entrada que coincide con uno de los globos de más arriba es un archivo normal y prueba si la entrada era una coincidencia (si uno de los globos no coincide, el patrón permanece como está). Si desea contar todas las entradas en los directorios dados independientemente de su tipo, reemplace-f
con-e
.Ksh tiene una manera de hacer que los patrones coincidan con los archivos de puntos y producir una lista vacía en caso de que ningún archivo coincida con un patrón. Entonces, en ksh puedes contar archivos regulares como este:
o todos los archivos simplemente así:
Bash tiene diferentes formas de hacer esto más simple. Para contar archivos regulares:
Para contar todos los archivos:
Como de costumbre, es aún más simple en zsh. Para contar archivos regulares:
Cambie
(DN.)
a(DN)
para contar todos los archivos.¹ Tenga en cuenta que cada patrón coincide, de lo contrario, los resultados podrían estar apagados (por ejemplo, si está contando archivos que comienzan con un dígito, no puede hacerlo simplemente
for x in [0-9]*; do if [ -f "$x" ]; then …
porque puede haber un archivo llamado[0-9]foo
).fuente
Basado en un guión de conteo , la respuesta de Shawn y un truco de Bash para asegurarse de que incluso los nombres de archivo con líneas nuevas se impriman en forma utilizable en una sola línea:
printf %q
es imprimir una versión citada de una cadena, es decir, una cadena de una sola línea que podría poner en una secuencia de comandos Bash para interpretarla como una cadena literal que incluye (potencialmente) líneas nuevas y otros caracteres especiales. Por ejemplo, véaseecho -n $'\tfoo\nbar'
vsprintf %q $'\tfoo\nbar'
.El
find
comando funciona simplemente imprimiendo un solo carácter para cada archivo y luego contando esos en lugar de contar líneas.fuente
Aquí está una manera "fuerza bruta" -ish para obtener su resultado, utilizando
find
,echo
,ls
,wc
,xargs
yawk
.fuente
fuente
fuente