Limite POSIX encontra profundidade específica?

11

Percebi recentemente que as especificações POSIX para find não incluem o -maxdepth primário.

Para aqueles que não estão familiarizados com ele, a finalidade do -maxdepth primary é restringir quantos níveis de profundidade find irão descer. -maxdepth 0 resulta em apenas argumentos de linha de comando sendo processados; -maxdepth 1 só lida com resultados diretamente dentro dos argumentos da linha de comando, etc.

Como posso obter o comportamento equivalente ao não-POSIX -maxdepth primary usando apenas opções e ferramentas especificadas por POSIX?

(Nota: É claro que posso obter o equivalente a -maxdepth 0 usando apenas -prune como o primeiro operando, mas isso não se estende a outras profundidades).

    
por Wildcard 11.04.2016 / 08:14

2 respostas

5

Você pode usar -path para corresponder a uma determinada profundidade e podar lá. Por exemplo

find . -path '*/*/*' -prune -o -type d -print

seria maxdepth 1, pois * corresponde ao . , */* corresponde a ./dir1 e */*/* corresponde a ./dir1/dir2 que é removido. Se você usar um diretório inicial absoluto, precisará adicionar um / inicial ao -path .

    
por 11.04.2016 / 09:43
7

A abordagem do meuh é ineficiente, pois sua abordagem -maxdepth 1 ainda permite que find leia o conteúdo dos diretórios no nível 1 para depois ignorá-los. Ele também não funcionará corretamente com algumas implementações de find (incluindo GNU find ) se alguns nomes de diretório contiverem seqüências de bytes que não formam caracteres válidos no código do idioma do usuário (como para nomes de arquivos em uma codificação de caracteres diferente).

find . \( -name . -o -prune \) -extra-conditions-and-actions

é a maneira mais canônica de implementar o -maxdepth 1 do GNU (ou -depth -2 do FreeBSD).

Geralmente, é -depth 1 que você quer ( -mindepth 1 -maxdepth 1 ) porque você não quer considerar . (profundidade 0), e então é ainda mais simples:

find . ! -name . -prune -extra-conditions-and-actions

Para -maxdepth 2 , isso se torna:

find . \( ! -path './*/*' -o -prune \) -extra-conditions-and-actions

E é aí que você executa os problemas de caractere inválido.

Por exemplo, se você tiver um diretório chamado Stéphane , mas que é esteja codificado no conjunto de caracteres iso8859-1 (também conhecido como latin1) (0xe9 byte), como era mais comum na Europa Ocidental e na América até meados 2000s, então esse 0xe9 byte não é um caractere válido em UTF-8. Portanto, nas localidades UTF-8, o curinga * (com algumas find implementações) não corresponderá a Stéphane , pois * é 0 ou mais caracteres e 0xe9 não é um caractere.

$ locale charmap
UTF-8
$ find . -maxdepth 2
.
./St?phane
./St?phane/Chazelas
./Stéphane
./Stéphane/Chazelas
./John
./John/Smith
$ find . \( ! -path './*/*' -o -prune \)
.
./St?phane
./St?phane/Chazelas
./St?phane/Chazelas/age
./St?phane/Chazelas/gender
./St?phane/Chazelas/address
./Stéphane
./Stéphane/Chazelas
./John
./John/Smith

Meu find (quando a saída vai para um terminal) exibe esse byte 0xe9 inválido como ? acima. Você pode ver que St<0xe9>phane/Chazelas não foi prune d.

Você pode contornar isso fazendo:

LC_ALL=C find . \( ! -path './*/*' -o -prune \) -extra-conditions-and-actions

Mas observe que isso afeta todas as configurações de localidade de find e qualquer aplicativo executado (como os predicados -exec ).

$ LC_ALL=C find . \( ! -path './*/*' -o -prune \)
.
./St?phane
./St?phane/Chazelas
./St??phane
./St??phane/Chazelas
./John
./John/Smith

Agora, eu realmente obtenho um -maxdepth 2 , mas observe como o é no segundo Stéphane corretamente codificado em UTF-8 é exibido como ?? como os bytes 0xc3 0xa9 (considerados como dois caracteres indefinidos individuais no idioma C) da codificação UTF-8 de é não são caracteres imprimíveis na localidade C.

E se eu tivesse adicionado um -name '????????' , eu teria recebido o Stéphane errado (o codificado em iso8859-1).

Para aplicar a caminhos arbitrários em vez de . , você faria:

find some/dir/. ! -name . -prune ...

para -mindepth 1 -maxdepth 1 ou:

find some/dir/. \( ! -path '*/./*/*' -o -prune \) ...

para -maxdepth 2 .

Eu ainda faria um:

(cd -P -- "$dir" && find . ...)

Primeiro, porque isso torna os caminhos mais curtos, o que torna menos provável que aconteça em caminho muito longo ou lista de args por muito tempo , mas também para contornar o fato de que find não suporta argumentos de caminho arbitrário (exceto com -f com FreeBSD find ), já que ele irá bloquear os valores de $dir como ! ou -print ...

O -o em combinação com a negação é um truque comum para executar dois conjuntos independentes de -condition / -action em find .

Se você deseja executar -action1 nos arquivos que atendem -condition1 e independentemente -action2 nos arquivos que atendem -condition2 , não é possível fazer isso:

find . -condition1 -action1 -condition2 -action2

Como -action2 seria executado apenas para arquivos que atendem às condições ambos .

Nor:

find . -contition1 -action1 -o -condition2 -action2

Como -action2 não seria executado para arquivos que atendem às condições ambas .

find . \( ! -condition1 -o -action1 \) -condition2 -action2

funciona como \( ! -condition1 -o -action1 \) resolveria para true para cada arquivo. Isso pressupõe que -action1 é uma ação (como -prune , -exec ... {} + ) que sempre retorna true . Para ações como -exec ... \; que podem retornar false , você pode adicionar outro -o -something onde -something é inofensivo, mas retorna verdadeiro como -true no GNU find ou -links +0 ou -name '*' (no entanto, observe o problema sobre caracteres inválidos acima).

    
por 14.12.2016 / 17:31

Tags