Linux encontrar-type d: como listar os diretórios mais profundos, como ./dir1/dir2 sem ./dir1?

Linux encontrar-type d: como listar os diretórios mais profundos, como ./dir1/dir2 sem ./dir1?
find . -type d

normalmente irá mostrar

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

Eu só quero o

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

, sem seu pai ./dir1, ...

Em outras palavras, quero apenas ver diretórios que não contenham subdiretórios.

Qualquer ideia?

EDIT: descobri que -links 2funciona em um ambiente Linux normal, por exemplo,

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

funciona perfeitamente.

Porém, quando eu monto um diretório (do MacOS ou Windows) no contêiner do docker, as coisas mudam, não funciona, você pode tentar isto:

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

Responder1

Com zsh:

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

Ou diretamente sem a função intermediária:

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

Isso é:

  • fn() some-commanddefine uma fnfunção some-commandcomo o corpo, como no Bourne-shell e na maioria dos shells do tipo Bourne.
  • () { code } argsuma função anônima que é executada codecomo argsparâmetro posicional.
  • (( $# ))aqui o corpo dessa função anônima é uma expressão aritmética que resolve paraverdadeiroif $#(o número de argumentos) for diferente de zero.
  • ${param-default}(do shell Bourne) se expande para $paramse o parâmetro estiver definido ou defaultnão. Aqui com ${1-$REPLY}, permitimos que nossa função seja chamada diretamente (como has_subdirs mydir) ou como uma função qualificadora globbing como abaixo.
  • $dir/*(ND/Y1)expande para os arquivos do tipo diretório em $dir( /) incluindo os ocultos ( Dotglob) e não gera erros se não houver correspondência ( Nullglob). Mas com Y1, paramos na primeira partida. Portanto, a função anônima (e, portanto has_subdirs, ) retornará true se o diretório contiver pelo menos um subdiretório.
  • print -rC1imprime seus argumentos raw no 1 Column
  • ^+has_subdirsrestringe a arquivos para os quais a has_subdirsfunção ( ) não ^retorna verdadeiro.

Se você tiver que usar o bashshell, basta fazer:

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

Ou para armazenar o resultado em um array bash (precisa do bash-4.4+):

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

(usando registros delimitados por NUL para poder armazenar caminhos arbitrários).

Se você não tem zsh, mas tem perl, você poderia fazer:

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

Ou se você tiver GNU awk:

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

Aqueles findimprimiram os diretóriosprofundidade primeiro(sai antes dos galhos em que estão) e obtém perl/ awkpara imprimir os registros que não foram encontrados seguidos de /no início do registro anterior.

Novamente, para armazenar os arquivos em um array bash 4.4+, você deseja mudar para registros delimitados por NUL na saída movendo o -lafter -0in perlou add -v 'ORS=\0'in 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}'
)

Em alguns sistemas e sistemas de arquivos (como ext4sistemas de arquivos em sistemas baseados em Linux), você pode confiar no fato de que os diretórios que possuem subdiretórios têm uma contagem de links maior que 2 ( dire dir/.um link adicional para cada dir/subdir/..).

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

Ou usando findem qualquer shell:

find . -type d -links -3

Isso não funcionará btrfs(que foi substituído ext4como sistema de arquivos padrão em várias distribuições Linux), por exemplo, onde a contagem de links de diretórios é sempre uma, então eu não recomendaria isso como uma solução geral.

O mesmo para sistemas de arquivos union, como é o seu caso, como overlayfsonde encontro algunsmescladoos diretórios têm uma contagem de links igual a 1, independentemente de conterem subdiretórios ou não.

No entanto, onde quer que seja utilizável, tem a vantagem sobre as soluções que contam manualmente os subdiretórios de que você não precisa de acesso de leitura ao diretório (apenas acesso de pesquisa ao seu pai).

Responder2

O que você procura é:

find . -type d -links 2

Cada diretório no sistema de arquivos possui pelo menos dois links físicos - o próprio diretório no pai e '.' entrada. Cada entrada '..' nos subdiretórios adiciona um novo link físico ao diretório. Então você só precisa encontrar diretórios com dois links físicos.

O comando sugerido em outra resposta

find . -type d -links -3

Faz o mesmo, mas com a declaração "diretórios com menos de três links físicos para ele".

Responder3

O mais sortido:

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

Em uma linha

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

Como a operação mencionou:

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

Responder4

Ler man find. A -mindepthopção é o que você deseja.

informação relacionada