
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 -print0
em 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 globstar
opção; também definido dotglob
para 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 -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 h:/g
está 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 find
a imprimir sua saída delimitada por caracteres NUL (0x00) e read
a 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.