Necesito inspeccionar todos los subdirectorios e informar cuántos archivos (sin recurrencia adicional) contienen:
directoryName1 numberOfFiles
directoryName2 numberOfFiles
Necesito inspeccionar todos los subdirectorios e informar cuántos archivos (sin recurrencia adicional) contienen:
directoryName1 numberOfFiles
directoryName2 numberOfFiles
Respuestas:
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 $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
es find ./hello, buddy
. Estás diciendo find
que 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 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.
-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
:
find dir1/ dir2/ -maxdepth 1 -type f | wc -l
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 canaliza wc
para contar el número de esas líneas.
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:
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 -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:
FIGNORE='.?(.)'
count=0
for x in ~(N)directoryName1/* ~(N)directoryName2/*; do
if [ -f "$x" ]; then ((++count)); fi
done
o todos los archivos simplemente así:
FIGNORE='.?(.)'
files=(~(N)directoryName1/* ~(N)directoryName2/*)
count=${#files}
Bash tiene diferentes formas de hacer esto más simple. Para contar archivos regulares:
shopt -s dotglob nullglob
count=0
for x in directoryName1/* directoryName2/*; do
if [ -f "$x" ]; then ((++count)); fi
done
Para contar todos los archivos:
shopt -s dotglob nullglob
files=(directoryName1/* directoryName2/*)
count=${#files}
Como de costumbre, es aún más simple en zsh. Para contar archivos regulares:
files=({directoryName1,directoryName2}/*(DN.))
count=$#files
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
).
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 %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éase echo -n $'\tfoo\nbar'
vs printf %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.
Aquí está una manera "fuerza bruta" -ish para obtener su resultado, utilizando find
, echo
, ls
, wc
, xargs
y awk
.
find . -maxdepth 1 -type d -exec sh -c "echo '{}'; ls -1 '{}' | wc -l" \; | xargs -n 2 | awk '{print $1" "$2}'
for i in *; do echo $i; ls $i | wc -l; done
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..
)