Pode find
encontrar arquivos que foram criados enquanto estava andando no diretório? Sim, a menos que a implementação específica evite explicitamente isso lendo a lista de arquivos antes de fazer qualquer coisa. A definição POSIX de readdir()
não garante:
If a file is removed from or added to the directory after the most recent call to
opendir()
orrewinddir()
, whether a subsequent call toreaddir()
returns an entry for that file is unspecified.
Eu testei o find
no meu Debian (GNU find, versão do pacote Debian 4.6.0+git+20161106-2
). strace
mostrou que leu o diretório completo antes de fazer qualquer coisa.
Navegar no código-fonte um pouco mais faz parecer que o GNU find usa partes do gnulib para ler os diretórios, e existe isso em gnulib / lib / fts.c ( gl/lib/fts.c
no find
tarball):
/* If possible (see max_entries, below), read no more than this many directory
entries at a time. Without this limit (i.e., when using non-NULL
fts_compar), processing a directory with 4,000,000 entries requires ~1GiB
of memory, and handling 64M entries would require 16GiB of memory. */
#ifndef FTS_MAX_READDIR_ENTRIES
# define FTS_MAX_READDIR_ENTRIES 100000
#endif
Alterei esse limite para 100 e fiz
mkdir test; cd test; touch {0000..2999}.foo
find . -type f -exec sh -c 'mv "$1" "${1%.foo}.barbarbarbarbarbarbarbar"' sh {} \; -print
resultando em resultados hilariantes como este arquivo (ele foi renomeado cinco vezes):
1046.barbarbarbarbarbarbarbar.barbarbarbarbarbarbarbar.barbarbarbarbarbarbarbar.barbarbarbarbarbarbarbar.barbarbarbarbarbarbarbar
Obviamente, um diretório muito grande (mais de 100.000 entradas) seria necessário para disparar esse efeito em uma construção padrão do GNU find, mas um loop de processo readdir + sem cache seria ainda mais vulnerável.
No entanto, no Linux, o readdir()
na biblioteca C é implementado por meio da chamada de sistema getdents()
, que retorna várias entradas de diretório de uma só vez. O que significa que as chamadas posteriores para readdir()
podem retornar arquivos que já foram removidos, mas, para diretórios muito pequenos, você obteria efetivamente um instantâneo do estado inicial. Eu não sei sobre outros sistemas.
Eu fiz as renomeações para um nome de arquivo maior de propósito: para evitar que o nome fosse substituído no local. Não importa, o mesmo teste em uma renomeação de mesmo tamanho também fez renomeações duplas e triplas. Se e como isso importa, é claro, dependerá dos componentes internos do sistema de arquivos.
Pode ser inteligente evitar isso fazendo com que a expressão find
não corresponda aos arquivos que já foram processados. Isto é, para adicionar -name "*.foo"
no exemplo.