Eu resolvi a sed
resposta pouco tempo depois de postar esta pergunta; ninguém mais usou sed
até agora, aqui está:
sed '$!N;/^\(.*\)\n\1$/d;P;D'
Um pouco de brincadeira com o problema mais geral (e a exclusão de linhas em conjuntos de três? Ou quatro ou cinco?) Forneceu a seguinte solução extensível:
sed -e ':top' -e '$!{/\n/!{N;b top' -e '};};/^\(.*\)\n\1$/d;P;D' temp
Estendido para remover triplos de linhas:
sed -e ':top' -e '$!{/\n.*\n/!{N;b top' -e '};};/^\(.*\)\n\1\n\1$/d;P;D' temp
Ou para remover quads de linhas:
sed -e ':top' -e '$!{/\n.*\n.*\n/!{N;b top' -e '};};/^\(.*\)\n\1\n\1\n\1$/d;P;D' temp
sed
possui uma vantagem adicional sobre a maioria das outras opções, que é a capacidade de operar verdadeiramente em um fluxo, sem mais memória necessária do que o número real de linhas a serem verificadas quanto a duplicatas.
Como o cuonglm apontou nos comentários , é necessário definir o código de idioma para C para evitar falhas na remoção adequada das linhas que contêm caracteres de vários bytes. Portanto, os comandos acima se tornam:
LC_ALL=C sed '$!N;/^\(.*\)\n\1$/d;P;D' temp
LC_ALL=C sed -e ':top' -e '$!{/\n/!{N;b top' -e '};};/^\(.*\)\n\1$/d;P;D' temp
LC_ALL=C sed -e ':top' -e '$!{/\n.*\n/!{N;b top' -e '};};/^\(.*\)\n\1\n\1$/d;P;D' temp
# Etc.
C
, caso contrário, no código de vários bytes, o caractere inválido nesse código de idioma causa a falha do comando.