
Eu tenho um arquivo chamado test
contendo
test
test
e estou executando este comando
while read line
do
echo "$line"
done </tmp/test
Isso deve gerar "teste" duas vezes, mas só gera uma vez. Usar while IFS= read -r line
não muda nada. Algo que corrige isso é gerar uma terceira linha vazia, mas deve funcionar de qualquer maneira.
Responder1
Tentar:
while IFS='' read -r line || [ "$line" ]
Sim, está lendo da entrada padrão (stdin), o <test
redirecionamento vai para stdin.
O que pode estar acontecendo é que read foi projetado para funcionar com arquivos de texto.Arquivos de texto corretos devem terminar em uma nova linha.
Fazer umo final do arquivo em uma nova linha é muito rápido:
[ -n "$(tail -c1 file)" ] && printf '\n' >>file
Mas se não for possível editar o arquivo para adicionar uma nova linha (se estiver faltando), a solução é testar (adicionalmente) se algo foi lido:
while read line || [ "$line" ]
Isso fará com que o loop seja executado se a leitura falhar (chegou ao final do arquivo sem ler uma nova linha (por padrão)), mas algo foi lido em (in "$line"
).
Isso funcionará corretamente para arquivos que terminam em nova linha ou não.
Não, a IFS='' read
não afetará (diretamente) a leitura da última linha; na verdade, o IFS=''
(em comparação com um IFS padrão[a]de espaço, tabulação, nova linha) afetará apenas a divisão emdiversosvariáveis (além de afetar a remoção de espaços em branco iniciais e/ou finais (se o IFS contiver apenas espaços em branco[b])). Como existe apenas uma variável ( "$line"
) não há divisão a ser realizada.
A -d
opção de leitura (no bash desde 2.04) também não ajudará. Não há nenhum caractere específico de "fim de arquivo" para corresponder (o último byte pode ser qualquer um).
As únicas opções restantes são:
- reparar o arquivo para torná-lo um arquivo de texto correto
- teste se algo foi lido na variável
line
.
O script correto, então, será:
#!/bin/bash
while IFS='' read -r line || [ "$line" ]
do
printf '%s\n' "$line"
done <test
IFS='' usado para evitar problemas com valores incomuns de IFS.
[a][b]Claro, se IFS tiver algum valor, muitos outros caracteres podem ser removidos. Try (para valores que terminam em 'x')
printf "test\ntesx\ntest" | while IFS="x" read -r line || [ "$line" ]; do echo "$line"; done
Não, zsh faz algo diferente.