Dans les commentaires de cette question, un cas est survenu où diverses implémentations sed n'étaient pas d'accord sur un programme assez simple, et nous (ou du moins je) n'avons pas été en mesure de déterminer ce que la spécification requiert réellement pour cela.
Le problème est le comportement d'une plage commençant à une ligne supprimée:
1d;1,2d
La ligne 2 doit-elle être supprimée même si le début de la plage a été supprimé avant d'atteindre cette commande? Mon attente initiale était "non" en ligne avec BSD sed, tandis que GNU sed dit "oui", et la vérification du texte de spécification ne résout pas entièrement le problème.
Correspondant à mes attentes sont (au moins) macOS et Solaris sed
, et BSD sed
. En désaccord sont (au moins) GNU et Busybox sed
, et de nombreuses personnes ici. Les deux premiers sont certifiés SUS tandis que les autres sont probablement plus répandus. Quel comportement est correct?
Le texte de spécification pour les plages à deux adresses indique:
L' utilitaire sed doit ensuite appliquer en séquence toutes les commandes dont les adresses sélectionnent cet espace de modèle, jusqu'à ce qu'une commande démarre le cycle suivant ou se termine.
et
Une commande d'édition avec deux adresses doit sélectionner la plage inclusive du premier espace de modèle qui correspond à la première adresse au prochain espace de modèle qui correspond à la seconde. [...] À partir de la première ligne suivant la plage sélectionnée, sed recherchera à nouveau la première adresse. Par la suite, le processus doit être répété.
On peut soutenir que la ligne 2 est dans "la plage inclusive du premier espace de modèle qui correspond à la première adresse au prochain espace de modèle qui correspond à la seconde", que le point de départ ait été supprimé ou non. D'un autre côté, je m'attendais à ce que le premier d
passe au cycle suivant et ne donne pas à la gamme une chance de commencer. Les implémentations certifiées UNIX ™ font ce que j'attendais, mais potentiellement pas ce que la spécification exige.
Certaines expériences illustratives suivent, mais la question clé est la suivante : ce qui devrait sed
faire quand une gamme commence sur une ligne supprimée?
Expériences et exemples
Voici une démonstration simplifiée du problème, qui imprime des copies supplémentaires de lignes plutôt que de les supprimer:
printf 'a\nb\n' | sed -e '1d;1,2p'
Cela fournit sed
deux lignes d'entrée, a
et b
. Le programme fait deux choses:
Supprime la première ligne avec
1d
. Lad
commande seraSupprimez l'espace de motif et commencez le cycle suivant. et
- Sélectionnez la plage de lignes de 1 à 2 et imprimez-les explicitement, en plus de l'impression automatique reçue par chaque ligne. Une ligne incluse dans la plage doit donc apparaître deux fois.
Je m'attendais à ce que cela imprime
b
uniquement, la plage ne s'appliquant pas car elle 1,2
n'est jamais atteinte pendant la ligne 1 (car elle est déjà d
passée au cycle / ligne suivant) et donc l'inclusion de la plage ne commence jamais, alors qu'elle a
a été supprimée. Les Unix conformes sed
de macOS et de Solaris 10 produisent cette sortie, tout comme les non-POSIX sed
dans Solaris et BSD sed
en général.
GNU sed, d'autre part, imprime
b
b
indiquant qu'il a interprété la plage. Cela se produit à la fois en mode POSIX et non. Sed de Busybox a le même comportement (mais pas toujours un comportement identique, donc il ne semble pas être le résultat d'un code partagé).
Expérimentation supplémentaire avec
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'
constate qu'il semble traiter une plage commençant à une ligne supprimée comme si elle commençait sur la ligne suivante . Ceci est visible car /c/
ne correspond pas à la fin de la plage. Utiliser /b/
pour démarrer la plage ne se comporte pas de la même manière que 2
.
L'exemple de travail initial que j'utilisais était
printf '%s\n' a b c d e | sed -e '1{/a/d;};1,//d'
comme un moyen de supprimer toutes les lignes jusqu'à la première /a/
correspondance, même si c'est sur la première ligne (ce que GNU sed utiliserait 0,/a/d
- c'était une tentative de rendu compatible POSIX de cela).
Il a été suggéré que cela devrait à la place supprimer jusqu'à la deuxième correspondance de /a/
si la première ligne correspond (ou le fichier entier s'il n'y a pas de deuxième correspondance), ce qui semble plausible - mais encore une fois, seul GNU sed le fait. MacOS sed et sed de Solaris produisent
b
c
d
e
pour cela, comme je m'y attendais (GNU sed produit la sortie vide en supprimant la plage non terminée; Busybox sed imprime juste d
et e
, ce qui est clairement faux quoi qu'il arrive). En général, je suppose que le fait d'avoir réussi les tests de conformité de la certification signifie que leur comportement est correct, mais suffisamment de personnes ont suggéré sinon que je ne suis pas sûr, le texte de spécification n'est pas complètement convaincant et la suite de tests ne peut pas être parfaitement complet.
De toute évidence, il n'est pas pratiquement portable d'écrire ce code aujourd'hui étant donné l'incohérence, mais théoriquement, il devrait être équivalent partout avec une signification ou une autre. Je pense que c'est un bug, mais je ne sais pas contre quelle (s) implémentation (s) le signaler. Mon avis est actuellement que le comportement de GNU et de Busybox sed est incompatible avec la spécification, mais je peux me tromper à ce sujet.
Que requiert POSIX ici?
ed
, en contournantsed
complètement?