Directorios con dos o más archivos

11

Quiero encontrar un subdirectorio del directorio actual, que (ese es el subdirectorio) contiene 2 o más archivos regulares.

No estoy interesado en directorios que contengan menos de 2 archivos, ni en directorios que contengan solo subdirectorios.

porton
fuente

Respuestas:

12

Aquí hay un enfoque completamente diferente basado en GNU findy uniq. Esto es mucho más rápido y amigable con la CPU que las respuestas basadas en la ejecución de un comando de shell que cuenta archivos para cada directorio encontrado.

find . -type f -printf '%h\n' | sort | uniq -d

El findcomando imprime el directorio de todos los archivos en la jerarquía y uniqsolo muestra los directorios que aparecen al menos dos veces.

xhienne
fuente
2
No debe analizar la salida de find. En este caso, porque GNU finddestrozará los nombres de directorios que tienen caracteres que no se pueden imprimir en la configuración regional actual (como "ä" en la configuración regional en C). Consulte también unix.stackexchange.com/questions/321697/…
Kusalananda
44
@Kusalananda, no cuando la salida no llega a un tty. Aquí, el único problema es con los caracteres de nueva línea, que se puede solucionar mediante el uso-printf '%h\0' | sort -z | uniq -zd | xargs -r0 ...
Stéphane Chazelas
6
find . -type d \
    -exec sh -c 'c=0; for n in "$1"/*; do [ -f "$n" ] && [ ! -h "$n" ] && c=$(( c + 1 )); done; [ "$c" -ge 2 ]' sh {} ';' \
    -print

Esto encontrará todos los nombres dentro o debajo del directorio actual y luego filtrará todos los nombres que no sean nombres de directorios.

Los nombres de directorio restantes se darán a este breve script:

c=0
for n in "$1"/*; do
    [ -f "$n" ] && [ ! -h "$n" ] && c=$(( c + 1 ))
done

[ "$c" -ge 2 ]

Este script contará el número de archivos regulares (omitiendo enlaces simbólicos) en el directorio dado como el primer argumento de línea de comando (desde find). El último comando en el script es una prueba para ver si el recuento fue 2 o mayor. El resultado de esta prueba es el valor de retorno (estado de salida) del script.

Si la prueba tuvo éxito, -printhará findque se imprima la ruta al directorio.

Para considerar también los archivos ocultos (archivos cuyos nombres comienzan con un punto), cambie la sh -csecuencia de comandos de decir

for n in "$1"/*; do

a

for n in "$1"/* "$1"/.*; do

Pruebas:

$ tree
.
`-- test
    |-- a
    |-- dir1
    |   |-- a
    |   |-- b
    |   `-- c
    `-- dir2
        |-- dira
        |-- dirb
        |   |-- file-1
        |   `-- file-2
        `-- dirc

6 directories, 6 files

$ find . -type d -exec sh -c 'c=0; for n in "$1"/*; do [ -f "$n" ] && [ ! -h "$n" ] && c=$(( c + 1 )); done; [ "$c" -ge 2 ]' sh {} ';' -print
./test/dir1
./test/dir2/dirb
Kusalananda
fuente
Su solución no cuenta los archivos con un nombre que comienza con un punto. También debe inicializar c = 0 para evitar mensajes de error con directorios que no contienen ningún archivo.
xhienne
@xhienne consideré archivos ocultos y agregaré una nota al respecto. No hay error si no hay archivos regulares en un directorio ya que [ "" -ge 2 ]es una prueba válida.
Kusalananda
No estoy seguro de cómo definir "válido". POSIX requiere que arg1 sea un valor entero. dash, bash --posixy testtodos muestran un mensaje de error y salen con 2 (es decir, "Se produjo un error")
xhienne
@xhienne Ah, estaba probando en un sistema que kshfuncionaba como sh. Se enmendará de inmediato. ¡Gracias por molestarme! :-)
Kusalananda
Además, hace [ -f ... ]referencia a enlaces simbólicos. Debe agregar una prueba para eliminarlos, ya que la pregunta especifica que solo se deben contar los archivos normales.
xhienne
6

Con la ayuda de la respuesta de Gilles sobre SU y su reversa y algunas modificaciones, aquí está lo que necesita.

find . -type d -exec sh -c 'set -- "$1"/*;X=0; 
    for args; do [ -f "$args" ] && X=$((X+1)) ;done; [ "$X" -gt 1 ] ' _ {} \; -print

Árbol de directorios.

.
├── test
│   ├── dir1
│   │   ├── a
│   │   ├── b
│   │   └── c
│   ├── dir2
│   │   ├── dira
│   │   │   └── a file\012with\012multiple\012line
│   │   ├── dirb
│   │   │   ├── file-1
│   │   │   └── file-2
│   │   └── dirc
│   ├── diraa
│   ├── dirbb
│   ├── dircc
│   └── x
│   └── x1
│   └── x2
└── test2
    ├── dir3
    └── dir4

Resultado:

./test
./test/dir1
./test/dir2/dirb
αғsнιη
fuente
También tuve esto al principio, pero tendrás problemas con los directorios que contienen múltiples subdirectorios y archivos. Tampoco elimina los directorios que solo contienen subdirectorios.
Kusalananda
Realmente no lo resuelve. Encuentra testlos dir2directorios y en mi configuración de prueba (ver mi respuesta).
Kusalananda
Funciona para su ejemplo, pero agregue test/x1y test/x2como archivos también ... $1y $2serán directorios para test, y se perderá el directorio.
Kusalananda
@Kusalananda De ninguna manera encontré, excepto lo que respondiste, intenté cambiar alguna parte de mi comando para que no fuera un duplicado exacto del tuyo (no excluí los archivos ocultos como lo hiciste), disculpa.
αғsнιη
1
No se preocupe en absoluto :-)
Kusalananda
3

Otro enfoque find+ wc:

find path/currdir -maxdepth 1 -type d ! -empty ! -path "path/currdir" \
-exec sh -c 'count=$(find "$1" -maxdepth 1 -type f | wc -l); [ $count -ge 2 ]' _ {} \; -print

  • path/currdir - ruta a su directorio actual

  • -maxdepth 1- considere solo subcarpetas secundarias directas

  • ! -empty - ignorar subcarpetas vacías

  • ! -path "path/currdir" - ignorar la ruta actual del directorio

  • count=$(find "$1" -maxdepth 1 -type f | wc -l)- countse le asigna el número de archivos para cada subcarpeta encontrada

  • [ $count -ge 2 ] ... -print - imprimir nombre / ruta de subcarpeta que contiene 2 o más archivos regulares

RomanPerekhrest
fuente