resumo
Imprimir linhas sem nova linha, adicione uma nova linha somente se houver outra linha para imprimir.
$ printf 'one\ntwo\n' |
awk '{ printf( "%s%s" , NR>1?"\n":"" , $0 ) }'; echo " done"
one
two done
Outras soluções
Se estivéssemos trabalhando com um arquivo, podemos truncar apenas um caractere (se terminar em uma nova linha):
removeTrailNewline () {[[$ (tail -c 1 "$ 1")]] || truncar -s-1 "$ 1"; }
Essa é uma solução rápida, pois precisa ler apenas um caractere do arquivo e removê-lo diretamente ( truncate
) sem ler o arquivo inteiro.
No entanto, ao trabalhar com dados de stdin (um fluxo), os dados devem ser lidos, todos eles. E é "consumido" assim que é lido. Sem retorno (como no truncado). Para encontrar o final de um fluxo, precisamos ler o final do fluxo. Nesse ponto, não há como voltar ao fluxo de entrada, os dados já foram "consumidos". Isso significa que os dados devem ser armazenados em alguma forma de buffer até correspondermos ao final do fluxo e, em seguida, fazer algo com os dados no buffer.
A solução mais óbvia é converter o fluxo em um arquivo e processá-lo. Mas a pergunta pede algum tipo de filtro do fluxo. Não é sobre o uso de arquivos adicionais.
variável
A solução ingênua seria capturar toda a entrada em uma variável:
FilterOne(){ filecontents=$(cat; echo "x"); # capture the whole input
filecontents=${filecontents%x}; # Remove the "x" added above.
nl=$'\n'; # use a variable for newline.
printf '%s' "${filecontents%"$nl"}"; # Remove newline (if it exists).
}
printf 'one\ntwo' | FilterOne ; echo 1done
printf 'one\ntwo\n' | FilterOne ; echo 2done
printf 'one\ntwo\n\n' | FilterOne ; echo 3done
memória
É possível carregar um arquivo inteiro na memória com o sed. No sed, é impossível evitar a nova linha à direita na última linha. O GNU sed pode evitar a impressão de uma nova linha à direita, mas apenas se o arquivo de origem já estiver em falta. Portanto, não, o simples sed não pode ajudar.
Exceto no GNU awk com a -z
opção:
sed -z 's/\(.*\)\n$/\1/'
Com awk (qualquer awk), solte o fluxo inteiro e printf
sem a nova linha à direita.
awk ' { content = content $0 RS }
END { gsub( "\n$", "", content ); printf( "%s", content ) }
'
Carregar um arquivo inteiro na memória pode não ser uma boa ideia, pode consumir muita memória.
Duas linhas na memória
No awk, podemos processar duas linhas por loop armazenando a linha anterior em uma variável e imprimindo a atual:
awk 'NR>1{print previous} {previous=$0} END {printf("%s",$0)}'
Processamento direto
Mas poderíamos fazer melhor.
Se imprimirmos a linha atual sem uma nova linha e só imprimirmos quando houver uma linha seguinte, processaremos uma linha por vez e a última linha não terá uma nova linha à direita:
awk 'NR == 1 {printf ("% s", $ 0); próximo}; {printf ("\ n% s", $ 0)} '
Ou, escrito de alguma outra maneira:
awk 'NR>1{ print "" }; { printf( "%s", $0 ) }'
Ou:
awk '{ printf( "%s%s" , NR>1?"\n":"" , $0 ) }'
Assim:
$ printf 'one\ntwo\n' | awk '{ printf( "%s%s" , NR>1?"\n":"" , $0 ) }'; echo " done"
one
two done
chomp
, poischomp
apenas exclui no máximo uma nova linha à direita.