Iterar recursivamente através de arquivos em um diretório

Iterar recursivamente através de arquivos em um diretório

A iteração recursiva através de arquivos em um diretório pode ser feita facilmente:

find . -type f -exec bar {} \;

No entanto, o procedimento acima não funciona para coisas mais complexas, onde muitas ramificações condicionais, loops, etc. precisam ser feitas. Eu costumava usar isso para o acima:

while read line; do [...]; done < <(find . -type f)

No entanto, parece que isso não funciona para arquivos que contêm caracteres obscuros:

$ touch $'a\nb'
$ find . -type f
./a?b

Existe uma alternativa que lide bem com personagens tão obscuros?

Responder1

Mais um uso parasegurofind:

while IFS= read -r -d '' -u 9
do
    [Do something with "$REPLY"]
done 9< <( find . -type f -exec printf '%s\0' {} + )

(Isso funciona com qualquer POSIX find, mas a parte do shell requer bash. Com *BSD e GNU find, você pode usar -print0em vez de -exec printf '%s\0' {} +, será um pouco mais rápido.)

Isso torna possível usar a entrada padrão dentro do loop e funciona comqualquercaminho.

Responder2

Fazer isso é tão simples quanto:

find -exec sh -c 'inline script "$0"' {} \;

Ou...

find -exec executable_script {} \;

Responder3

A abordagem mais simples (mas segura) é usar globbing de shell:

$ for f in *; do printf ":%s:\n" "$f"; done 
:a b:
:c
d:
:-e:
:e  f:
h:

Para fazer o recurso acima em subdiretórios (no bash), você pode usar a globstaropção; também definido dotglobpara corresponder aos arquivos cujo nome começa com .:

$ shopt -s globstar dotglob
$ for f in **/*; do printf ":%s:\n" "$f"; done 
:a b:
:c
d:
:-e:
:e  f:
:foo:
:foo/file1:
:foo/file two:
h:

Cuidado, até o bash 4.2, **/ele recorre a links simbólicos para diretórios. Desde o bash 4.3, **/ele recorre apenas a diretórios, como find.

Outra solução comum é usar find -print0com xargs -0:

$ touch -- 'a b' $'c\nd' $'e\tf' $'g\rh' '-e'
$ find . -type f -print0 | xargs -0 -I{} printf ":%s:\n" {}
h:/g
:./e    f:
:./a b:
:./-e:
:./c
d:

Observe que h:/gestá correto, pois o nome do arquivo contém uma extensão \r.

Responder4

É um pouco difícil fazer seu loop de leitura de forma portável, mas para o bash em particular você pode tentar algo comoesse.

Parte relevante:

while IFS= read -d $'\0' -r file ; do
        printf 'File found: %s\n' "$file"
done < <(find . -iname 'foo*' -print0)

Isso instrui finda imprimir sua saída delimitada por caracteres NUL (0x00) e reada buscar linhas delimitadas por NUL ( -d $'\0') sem manipular barras invertidas como escapes para outros caracteres ( -r) e não fazer nenhuma divisão de palavras nas linhas ( IFS=). Como 0x00 é um byte que não pode ocorrer em nomes de arquivos ou caminhos no Unix, isso deve resolver todos os seus problemas estranhos de nomes de arquivos.

informação relacionada