tl; dr
Faça loop em arquivos sem se preocupar com os espaços nos nomes dos arquivos:
while read file
do
echo "[$file]"
done < <(ls -1)
Resposta completa
Existem duas maneiras de fazer um loop em itens separados por quebra de linha, por exemplo, em uma lista de arquivos.
Suponha que os seguintes comandos sejam executados em um diretório que contenha esses arquivos:
$ ls -l
total 0
-rw-r--r-- 1 debona staff 0 17 oct 19:58 a file with spaces
-rw-r--r-- 1 debona staff 0 17 oct 22:51 common_file
O “for loop”, a maneira dolorosa
for file in `ls -1`
do
echo "[$file]"
done
Conforme o “loop for” divide a lista em cada um IFS
(o que não é a quebra de linha por padrão), ele imprime a seguinte saída:
[a]
[file]
[with]
[spaces]
[common_file]
OK, vamos definir a IFS
quebra de linha:
IFS='
' # Do you really think that IFS="n" could work?
for file in `ls -1`
do
echo "[$file]"
done
Sim, ele faz um loop em cada arquivo:
[a file with spaces]
[common_file]
Por que isso é doloroso? Normalmente, alterar o IFS
está se tornando difícil quando há loops aninhados. Se você precisar fazer um loop em uma lista de arquivos, terá que alterar IFS
novamente para aninhar um loop em itens separados por espaço dentro.
O “loop while”, a maneira mais fácil
ls -1 | while read file
do
echo "[$file]"
done
O read
shell embutido lê uma linha da entrada padrão. Tenha cuidado, ele pode ler a linha vazia enquanto o loop for ignora os itens vazios.
Como @gkalabin menciona nos comentários, neste caso, o corpo do loop while é executado em um subshell .
As duas armadilhas principais com o subshell:
- Todo o material dentro de um subshell não estará disponível após sua execução.
- Se você estiver
exit
dentro de um subshell, sairá do próprio subshell, não do “script principal”.
while
é uma palavra-chave shell que não se subdivide por si mesma. Em nosso caso, o corpo do loop while é uma subcamada por causa do tubo.
Para evitar canalizar a ls
saída na while
entrada, podemos usar um arquivo temporário:
- redirecionar a
ls
saída no arquivo - redirecionar o arquivo na
while
entrada - depois de fazer um loop nele, remova o arquivo
Felizmente, seu amado shell fornece uma maneira simples de fazer isso: a substituição do processo
<(ls -1) # this is the process substitution of the `ls - 1` command
echo <(ls -1) # it prints a path to a "special file" which contains the output of the command
# => /dev/fd/63
cat <(ls -1) # it prints the `ls -1` output
# => a file with spaces
# => common_file
Portanto, o loop while pode ser reescrito assim:
while read file
do
echo "[$file]"
done < <(ls -1)