while read loop para após a primeira linha, mesmo que não leia stdin

while read loop para após a primeira linha, mesmo que não leia stdin

Eu tenho um arquivo chamado testcontendo

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 linenã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 <testredirecionamento 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='' readnã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 -dopçã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.

informação relacionada