encontrar linux-tipo d: ¿cómo enumerar los directorios más profundos, como ./dir1/dir2 sin ./dir1?

encontrar linux-tipo d: ¿cómo enumerar los directorios más profundos, como ./dir1/dir2 sin ./dir1?
find . -type d

normalmente se mostrará

./dir1
./dir1/dir2
./dir3
./dir3/dir4
./dir3/dir4/dir5
...

solo quiero el

./dir1/dir2
./dir3/dir4/dir5

, sin su padre ./dir1, ...

En otras palabras, solo quiero ver directorios que no tengan subdirectorios.

¿Alguna idea?

EDITAR: encontré que -links 2funciona en un entorno Linux normal, por ejemplo,

docker run -it --rm ubuntu:bionic find /etc -type d -links 2

funciona perfectamente.

Sin embargo, cuando monto un directorio (de MacOS o Windows) en el contenedor de la ventana acoplable, las cosas cambiaron, no funciona, puedes probar esto:

docker run -it --rm -v /etc:/etc_of_host ubuntu:bionic find /etc_of_host -type d -links 2

Respuesta1

Con zsh:

has_subdirs() () (( $# )) ${1-$REPLY}/*(ND/Y1)
print -rC1 -- **/*(ND/^+has_subdirs)

O directamente sin la función intermediaria:

print -rC1 -- **/*(ND/^e['()(($#)) $REPLY/*(ND/Y1)'])

Eso es:

  • fn() some-commanddefine una fnfunción con some-commandel cuerpo como en el shell Bourne y en la mayoría de los shells similares a Bourne.
  • () { code } argsuna función anónima que se ejecuta codecomo argsparámetro posicional.
  • (( $# ))aquí el cuerpo de esa función anónima es una expresión aritmética que se resuelve enverdaderosi $#(el número de argumentos) es distinto de cero.
  • ${param-default}(desde el shell Bourne) se expande $paramsi el parámetro está configurado o defaultno. Aquí con ${1-$REPLY}, permitimos que nuestra función sea llamada directamente (como has_subdirs mydir) o como una función calificadora global como se muestra a continuación.
  • $dir/*(ND/Y1)se expande a los archivos de tipo directorio en $dir( /), incluidos los ocultos ( Dotglob), y no genera errores si no hay coincidencias ( Nullglob). Pero con Y1, nos detenemos en el primer partido. Entonces la función anónima (y por lo tanto has_subdirs) devolverá verdadero si el directorio contiene al menos un subdirectorio.
  • print -rC1imprime sus argumentos raw en 1 Cla columna
  • ^+has_subdirsse restringe a archivos para los cuales la has_subdirsfunción no ^devuelve ( ) verdadero.

Si tienes que usar el bashshell, simplemente haz:

zsh << 'EOF'
  print -rC1 -- **/*(ND/^e['()(($#)) $REPLY/*(ND/Y1)'])
EOF

O para almacenar el resultado en una matriz bash (necesita bash-4.4+):

readarray -td '' array < <(zsh << 'EOF'
  print -rNC1 -- **/*(ND/^e['()(($#)) $REPLY/*(ND/Y1)'])
EOF
)

(usando registros delimitados por NUL para poder almacenar rutas arbitrarias).

Si no tienes zsh, pero tienes perl, puedes hacer:

find . -depth -type d -print0 |
  perl -l -0ne 'print if rindex $prev, "$_/", 0; $prev = $_'

O si tienes GNU awk:

find . -depth -type d -print0 |
  gawk -v 'RS=\0' 'index(prev, $0"/") != 1; {prev = $0}'

Esos han findimpreso los directorios.profundidad primero(sale antes de las ramas en las que se encuentran) y obtiene perl/ awkpara imprimir los registros que no se encuentran seguidos /del inicio del registro anterior.

Nuevamente, para almacenar los archivos en una matriz bash 4.4+, querrá cambiar a registros delimitados NUL en la salida moviendo el -lafter -0adentro perlo -v 'ORS=\0'agregando gawk:

readarray -td array < <(
  find . -depth -type d -print0 |
    perl -0lne 'print if rindex $prev, "$_/", 0; $prev = $_'
)
readarray -td array < <(
  find . -depth -type d -print0 |
    gawk -v 'RS=\0' -v 'ORS=\0' 'index(prev, $0"/") != 1; {prev = $0}'
)

En algunos sistemas y sistemas de archivos (como ext4los sistemas de archivos en sistemas basados ​​en Linux), es posible que pueda confiar en el hecho de que los directorios que tienen subdirectorios tienen un número de enlaces superior a 2 ( diry dir/.un enlace adicional para cada uno dir/subdir/..).

print -rC1 -- **/*(ND/l-3)

O usando finden cualquier shell:

find . -type d -links -3

Eso no funcionará btrfs(que ha reemplazado ext4los sistemas de archivos predeterminados en varias distribuciones de Linux), por ejemplo, donde el número de enlaces de directorios es siempre uno, por lo que no lo recomendaría como solución general.

Lo mismo para los sistemas de archivos de unión como es su caso, como overlayfsdonde encuentro algunosfusionadoLos directorios tienen un recuento de enlaces de 1, ya sea que contengan subdirectorios o no.

Sin embargo, siempre que sea utilizable, tiene la ventaja sobre las soluciones que cuentan manualmente los subdirectorios de que no necesita acceso de lectura al directorio (solo acceso de búsqueda a su directorio principal).

Respuesta2

Lo que buscas es:

find . -type d -links 2

Cada directorio en el sistema de archivos tiene al menos dos enlaces físicos: el directorio en sí en el padre y '.' entrada. Cada entrada '..' en los subdirectorios agrega un nuevo enlace físico al directorio. Entonces sólo necesita encontrar directorios con dos enlaces físicos.

El comando sugerido en otra respuesta.

find . -type d -links -3

Hace lo mismo, pero con una declaración "directorios con menos de tres enlaces físicos".

Respuesta3

El más clasificado:

p="";(find . -type d;echo) | while read d; do [[ $p && $d != $p/* ]] && echo $p; p=$d; done

en una linea

a="";(find . -type d;echo )|while read i; do if  [[ (! $i =~ $a) && ("$a" != "") ]]; then echo $a; fi; a=$i;done

Como mencionó la operación:

a="";(find . -type d;echo )|while read i; do [[ $i != $a/* && ! -z "$a" ]] && echo $a; a=$i;done

Respuesta4

Leer man find. La -mindepthopción es la que quieras.

información relacionada