A saída de uma substituição de comando está sujeita à divisão de palavras (a qual você cuidou definindo IFS
), e globalização de nome de arquivo. A construção [abc]
é "corresponde a qualquer um dos caracteres a
, b
, c
" como de costume e
[2006_02_25].doc
corresponde a 0.doc
.
No Bash / ksh / zsh, você pode usar a estrela dupla para obter todos os arquivos na árvore de diretórios (recursivamente, não apenas no diretório atual). Isso deve encontrar os mesmos arquivos do seu exemplo:
shopt -s globstar # in Bash
# set -o globstar # in ksh
for file in **/* ; do
[[ -f $file ]] || continue # check it's a regular file, like find -type f
...
done
Naturalmente, find
é poderoso, então usá-lo pode ser mais fácil se você tiver muitas condições. Se fizer isso, você deve desabilitar o globbing de nomes de arquivo com set -f
além de corrigir IFS
:
set -f
IFS=$'\n'
for file in $(find -type f -some -other -conditions) ; do
...
done
ou use um loop while read
com substituição de processo:
while IFS= read -r file ; do
...
done < <(find -type f -some -other -conditions)
(O acima é semelhante a find ... | while ...
, mas ignora o problema da última parte de um pipeline sendo executado em um subshell.)
Ambos supõem que os nomes de arquivos não contêm novas linhas, pois são usados como separador para a saída de find
. (e porque $(..)
come novas linhas finais.)
No Bash, pelo menos, há uma maneira de fazer com que novas linhas de nomes funcionem também. Configurar o read
delimitador para a cadeia vazia faz com que ele use efetivamente o byte NUL como delimitador. Então:
while IFS= read -d '' -r file ; do
...
done < <(find -type f -some -other -conditions -print0)
Embora isso esteja começando a ficar difícil de escrever, já que você precisa de todas essas opções para read
para fazê-lo funcionar sem atrapalhar a entrada.
Quanto ao IFS
... Configurar IFS=$(echo -en "\n");
conjuntos IFS
para a cadeia vazia (porque a substituição de comando come a nova linha à direita), resultando na divisão não . A saída, neste caso, parece correta, já que você obtém toda a saída de find
de uma só vez, não linha por linha. Isso também mascara o problema com o globbing de nomes de arquivos, já que a string multilinha completa não corresponde a nenhum nome de arquivo e é passada como está.
Você pode ver a diferença se fizer qualquer outra coisa além de imprimir o valor do loop. Tente adicionar alguns separadores:
IFS=$(echo -en "\n") # same as IFS=
for FILE in $(find -type f); do echo "<$FILE>"; done
De longe, a maneira mais fácil de definir IFS
para uma nova linha em qualquer coisa, exceto nas conchas padrão mais básicas, é IFS=$'\n'
.