Recursivamente iterar através de arquivos em um diretório

14

A iteração recursiva por meio de arquivos em um diretório pode ser feita facilmente por:

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

No entanto, o acima não funciona para coisas mais complexas, onde muitas ramificações condicionais, looping 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?

    
por user2064000 26.06.2014 / 15:52

4 respostas

5

Ainda outro uso para seguro find :

while IFS= read -r -d '' -u 9
do
    [...]
done 9< <( find . -type f -exec printf '%s
while IFS= read -r -d '' -u 9
do
    [...]
done 9< <( find . -type f -exec printf '%s%pre%' {} + )
' {} + )

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

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

    
por 26.06.2014 / 17:51
9

Isso é tão simples quanto:

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

Ou ...

find -exec executable_script {} \;
    
por 26.06.2014 / 16:08
5

É um pouco difícil fazer seu loop de leitura portavelmente, mas para o bash em particular você pode tentar algo como isto .

Porção relevante:

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

Isso instrui find a imprimir sua saída delimitada por caracteres NUL (0x00) e read a buscar linhas delimitadas por NUL ( -d $'-r' ) sem manipular barras invertidas como escape para outros caracteres ( IFS= ) e não faça qualquer palavra dividida nas linhas ( %code% ). Como 0x00 é um byte que não pode ocorrer em nomes de arquivos ou caminhos no Unix, isso deve lidar com todos os seus problemas estranhos com nomes de arquivos.

    
por 26.06.2014 / 16:04
5

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

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

Para fazer o backup acima em subdiretórios (no bash), você pode usar a opção globstar ; também defina dotglob para corresponder 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:

Tenha em mente que até bash 4.2, **/ recorre a links simbólicos para diretórios. Desde bash 4.3, **/ recursa apenas em diretórios, como find .

Outra solução comum é usar find -print0 com 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 o h:/g está correto porque o nome do arquivo contém \r .

    
por 26.06.2014 / 16:18