Nos comentários a esta pergunta , surgiu um caso em que várias implementações sed discordavam de um programa bastante simples, e nós (ou pelo menos eu) não conseguimos determinar o que a especificação realmente exige para isso.
O problema é o comportamento de um intervalo que começa em uma linha excluída:
1d;1,2d
A linha 2 deve ser excluída mesmo que o início do intervalo tenha sido removido antes de atingir esse comando? Minha expectativa inicial era "não", de acordo com o BSD sed, enquanto o GNU sed diz "yes" e a verificação do texto da especificação não resolve completamente o problema.
Correspondendo às minhas expectativas estão (pelo menos) macOS sed
, Solaris e BSD sed
. Discordam são (pelo menos) GNU e Busybox sed
, e numerosas pessoas aqui. Os dois primeiros são certificados pelo SUS, enquanto os outros provavelmente são mais difundidos. Qual comportamento está correto?
O texto de especificação para intervalos de dois endereços diz:
O utilitário sed deve então aplicar em seqüência todos os comandos cujos endereços selecionam esse espaço padrão, até que um comando inicie o próximo ciclo ou saia.
e
Um comando de edição com dois endereços deve selecionar o intervalo inclusivo do primeiro espaço de padrão que corresponde ao primeiro endereço até o próximo espaço de padrão que corresponde ao segundo. [...] Começando na primeira linha após o intervalo selecionado, sed deve procurar novamente o primeiro endereço. Depois disso, o processo deve ser repetido.
Indiscutivelmente, a linha 2 está dentro "do intervalo inclusivo do primeiro espaço de padrão que corresponde ao primeiro endereço até o próximo espaço de padrão que corresponde ao segundo", independentemente de o ponto inicial ter sido excluído. Por outro lado, eu esperava que o primeiro d
passasse para o próximo ciclo e não desse ao intervalo uma chance de começar. As implementações certificadas com UNIX ™ fazem o que eu esperava, mas potencialmente não o que a especificação exige.
Seguem alguns experimentos ilustrativos, mas a questão principal é: o que deve ser sed
feito quando um intervalo começa em uma linha excluída?
Experiências e exemplos
Uma demonstração simplificada do problema é esta, que imprime cópias extras de linhas em vez de excluí-las:
printf 'a\nb\n' | sed -e '1d;1,2p'
Isso fornece sed
duas linhas de entrada a
e b
. O programa faz duas coisas:
Exclui a primeira linha com
1d
. Od
comando seráExclua o espaço do padrão e inicie o próximo ciclo. e
- Selecione o intervalo de linhas de 1 a 2 e as imprima explicitamente, além da impressão automática que toda linha recebe. Uma linha incluída no intervalo deve aparecer duas vezes.
Minha expectativa era que isso fosse impresso
b
somente, com o intervalo não sendo aplicado porque 1,2
nunca é atingido durante a linha 1 (porque d
já a
passou para o próximo ciclo / linha) e, portanto, a inclusão do intervalo nunca começa, enquanto foi excluída. Os Unix sed
s conformes do macOS e Solaris 10 produzem essa saída, assim como os não POSIX sed
no Solaris e BSD sed
em geral.
O GNU sed, por outro lado, imprime
b
b
indicando que ele tem interpretado o intervalo. Isso ocorre no modo POSIX e não. O sed do Busybox tem o mesmo comportamento (mas nem sempre o mesmo comportamento, por isso não parece ser resultado de código compartilhado).
Experimentação adicional com
printf 'a\nb\nc\nd\ne\n' | sed -e '2d;2,/c/p'
printf 'a\nb\nc\nd\ne\n' | sed -e '2d;2,/d/p'
descobre que parece tratar um intervalo que começa em uma linha excluída como se fosse iniciado na linha seguinte . Isso é visível porque /c/
não corresponde ao final do intervalo. Usar /b/
para iniciar o intervalo não se comporta da mesma forma que 2
.
O exemplo inicial de trabalho que eu estava usando era
printf '%s\n' a b c d e | sed -e '1{/a/d;};1,//d'
como uma maneira de excluir todas as linhas até a primeira /a/
correspondência, mesmo que isso esteja na primeira linha (o que o GNU sed usaria 0,/a/d
para - essa foi uma tentativa de versão compatível com POSIX).
Foi sugerido que isso exclua até a segunda correspondência de /a/
se a primeira linha corresponder (ou o arquivo inteiro, se não houver uma segunda correspondência), o que parece plausível - mas, novamente, apenas o GNU sed faz isso. Tanto o macOS sed como o sed do Solaris produzem
b
c
d
e
por isso, como eu esperava (o GNU sed produz a saída vazia da remoção do intervalo não terminado; o Busybox sed imprime apenas d
e e
, o que está claramente errado, não importa o quê). Geralmente, eu diria que a aprovação nos testes de conformidade da certificação significa que seu comportamento está correto, mas muitas pessoas sugeriram o contrário, não tenho certeza, o texto da especificação não é completamente convincente e a suíte de testes não pode ser perfeitamente abrangente.
Claramente, não é praticamente portátil escrever esse código hoje, dada a inconsistência, mas teoricamente deve ser equivalente em todo lugar a um significado ou outro. Eu acho que isso é um bug, mas não sei contra quais implementações denunciar. Minha opinião atualmente é que o comportamento do GNU e do Busybox sed é inconsistente com a especificação, mas eu posso estar enganado sobre isso.
O que o POSIX exige aqui?
ed
, ignorandosed
completamente?