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 2
funciona 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-command
define unafn
función consome-command
el cuerpo como en el shell Bourne y en la mayoría de los shells similares a Bourne.() { code } args
una función anónima que se ejecutacode
comoargs
pará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$param
si el parámetro está configurado odefault
no. Aquí con${1-$REPLY}
, permitimos que nuestra función sea llamada directamente (comohas_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 (D
otglob), y no genera errores si no hay coincidencias (N
ullglob). Pero conY1
, nos detenemos en el primer partido. Entonces la función anónima (y por lo tantohas_subdirs
) devolverá verdadero si el directorio contiene al menos un subdirectorio.print -rC1
imprime sus argumentosr
aw en1
C
la columna^+has_subdirs
se restringe a archivos para los cuales lahas_subdirs
función no^
devuelve ( ) verdadero.
Si tienes que usar el bash
shell, 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 find
impreso los directorios.profundidad primero(sale antes de las ramas en las que se encuentran) y obtiene perl
/ awk
para 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 -l
after -0
adentro perl
o -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 ext4
los 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 ( dir
y dir/.
un enlace adicional para cada uno dir/subdir/..
).
print -rC1 -- **/*(ND/l-3)
O usando find
en cualquier shell:
find . -type d -links -3
Eso no funcionará btrfs
(que ha reemplazado ext4
los 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 overlayfs
donde 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 -mindepth
opción es la que quieras.