Quel est l'intérêt d'utiliser plusieurs points d'exclamation dans sed?

12

La documentation de POSIX sed a déclaré:

Une fonction peut être précédée d'un ou plusieurs '!' caractères, auquel cas la fonction doit être appliquée si les adresses ne sélectionnent pas l'espace de modèle. Zéro ou plusieurs caractères <vides> doivent être acceptés avant le premier '!' personnage. Il n'est pas précisé si les caractères <vides> peuvent suivre un '!' et les demandes conformes ne doivent pas suivre un "!" caractère avec des caractères <vides>.

Ainsi, avec n'importe quel sed POSIX, nous pouvons:

sed -e '/pattern/!d' file

C'est la même chose que d'écrire:

sed -e '/pattern/!!d' file

Et !!!det ndes points d'exclamation sont encore très bien (Testé avec trois sedversions de heirloom toolchest ). Je ne vois aucun avantage entre plusieurs au lieu d'une seule exclamation.

Pourquoi la spécification a-t-elle permis cette syntaxe et comment est-elle utile dans une application réelle?


Il semble que GNU sed ne soit pas conforme dans ce cas, il se plaindra si nous utilisons plusieurs exclamations:

$ sed -e '/pattern/!!d' file
sed: -e expression #1, char 11: multiple `!'s
cuonglm
la source
2
FWIW: Sur OpenBSD !agit comme une bascule, /pattern/!!est le même que /pattern/et /pattern/!!!est le même que /pattern/!. Sur FreeBSD, plusieurs !sont identiques à un seul.
lcd047
2
Le point de beaucoup de choses dans la spécification est que les sedscripts peuvent être générés . Étant donné un POSIX sed, il devrait être très simple de créer un script pour l'écriture d'un sedscript. Et donc si vous aviez un déclencheur pour un cas qui devrait marquer une adresse !non digne de votre action, vous pourriez même le déclencher plusieurs fois pour le même et toujours sortir avec les mêmes résultats.
mikeserv
@cuonglm Non, seuls FreeBSD l'est. Les GNU, OpenBSD et NetBSD sedne le sont pas.
lcd047
@ lcd047: oui, bien sûr. Désolé pour mon mauvais anglais. Je veux dire que ce n'est pas conforme, n'est-ce pas? C'est bon de savoir ça. Mais le point principal de ma question est de savoir comment cette syntaxe peut être utile dans le monde réel, avec POSIX sed?
cuonglm
1
FWIW: un correctif a été validé dans OpenBSD-current.
lcd047

Réponses:

5

sedL'API est primitive - et c'est par conception. Au moins, il est resté primitif par sa conception - je ne peux pas dire s'il a été conçu primitivement au début. Dans la plupart des cas, l'écriture d'un sedscript qui, lorsqu'il est exécuté, produira un autre sedscript est en effet une question simple. sedest très souvent appliqué de cette manière par des préprocesseurs macro tels que m4et / ou make.

(Ce qui suit est un cas d'utilisation hautement hypothétique: il s'agit d'un problème conçu pour convenir à une solution. Si cela vous semble un peu exagéré, c'est probablement parce qu'il l'est, mais cela ne le rend pas nécessairement moins valide.)


Considérez le fichier d'entrée suivant:

cat <<"" >./infile
camel
cat dog camel
dog cat
switch
upper
lower

Si nous voulions écrire un sedscript qui ajouterait le mot- case à la fin de chaque mot approprié dans le fichier d'entrée ci-dessus uniquement s'il pouvait être trouvé sur une ligne dans le contexte approprié , et nous souhaitions le faire le plus efficacement possible ( comme cela devrait être notre objectif, par exemple, lors d'une opération de compilation), nous devons donc éviter autant que possible d' appliquer des /expressions rationnelles /.

Une chose que nous pourrions faire est de pré-éditer le fichier sur notre système en ce moment et de ne jamais appeler seddu tout pendant la compilation. Mais si l'un de ces mots dans le fichier doit ou ne doit pas être inclus en fonction des paramètres locaux et / ou des options de compilation, le faire ne serait probablement pas une alternative souhaitable.

Une autre chose que nous pourrions faire est de traiter le fichier maintenant contre les expressions rationnelles. Nous pouvons produire - et inclure dans notre compilation - un sedscript qui peut appliquer des modifications en fonction du numéro de ligne - ce qui est généralement un itinéraire beaucoup plus efficace à long terme.

Par exemple:

n=$(printf '\\\n\t')
grep -En 'camel|upper|lower' <infile |
sed "   1i${n%?}#!/usr/heirloom/bin/posix2001/sed -nf
        s/[^:]*/:&$n&!n;&!b&$n&/;s/://2;\$a${n%?}q"'
        s/ *cat/!/g;s/ *dog/!/g
        s| *\([cul][^ ]*\).*|s/.*/\1-case/p|'

... qui écrit la sortie sous forme de sedscript et qui ressemble à ...

#!/usr/heirloom/bin/posix2001/sed -nf
:1
    1!n;1!b1
    1s/.*/camel-case/p
:2
    2!n;2!b2
    2!!s/.*/camel-case/p
:5
    5!n;5!b5
    5s/.*/upper-case/p
:6
    6!n;6!b6
    6s/.*/lower-case/p
q

Lorsque cette sortie est enregistrée dans un fichier texte exécutable sur ma machine nommée ./bang.sedet exécutée comme ./bang.sed ./infile, la sortie est:

camel-case
upper-case
lower-case

Maintenant, vous pourriez me demander ... Pourquoi voudrais-je faire ça? Pourquoi ne devrais-je pas simplement ancrer grepdes matchs? Qui utilise la camel case de toute façon? Et à chaque question à laquelle je ne pouvais que répondre, je n'en ai aucune idée ... parce que non. Avant de lire cette question, je n'avais jamais personnellement remarqué le multi-! analyse syntaxique dans la spécification - je pense que c'est une prise assez soignée.

Le multi-! Cependant, cela a immédiatement eu un sens - une grande partie de la sedspécification est orientée vers des sed scripts simplement analysés et générés simplement . Vous trouverez probablement les \ndélimiteurs ewline requis pour [wr:bt{]avoir beaucoup plus de sens dans ce contexte, et si vous gardez cette idée à l'esprit, vous pourriez mieux comprendre certains autres aspects de la spécification - (comme l' :acceptation d'aucune adresse et le qrefus de accepter plus de 1) .

Dans l'exemple ci - dessus , je vous écris une certaine forme de sedscénario qui ne peut jamais être lu une fois. Si vous le regardez attentivement, vous remarquerez peut-être que lors de la sedlecture du fichier d'édition, il progresse d'un bloc de commande à l'autre - il ne se dérive jamais ou ne termine son script d'édition jusqu'à ce qu'il soit complètement terminé avec son fichier d'édition.

Je considère que multi-! les adresses pourraient être plus utiles dans ce contexte que dans certains autres, mais, en toute honnêteté, je ne peux pas penser à un seul cas dans lequel j'aurais pu l'utiliser à bon escient - et moi sedbeaucoup. Je pense également qu'il convient de noter que les deux GNU / BSD sedne parviennent pas à le gérer comme spécifié - ce n'est probablement pas un aspect de la spécification qui est très demandé, et donc si une implémentation l'ignore, je doute très sérieusement que leurs bugs @ box souffriront terriblement en conséquence.

Cela dit, le fait de ne pas gérer cela comme spécifié est un bogue pour toute implémentation qui prétend être conforme, et je pense donc que l'envoi d'un e-mail aux boîtes de développement pertinentes est nécessaire ici, et j'ai l'intention de le faire si vous ne le faites pas.

mikeserv
la source
1
Il est maintenant corrigé dans OpenBSD-current.
lcd047
1
Multiple !va être supprimé dans la prochaine spécification , que se passe-t-il ici!
cuonglm
@cuonglm - trop peu trop tard, je suppose. peut-être étais-je plus près de la marque que je ne le pensais.
mikeserv
@cuonglm - bien, ok, mais qu'est-ce que cela ... Accepté comme marqué signifie même?
mikeserv
1
@mikeserv: la réponse a expliqué mon émerveillement et m'a donné une autre vue avec l'API sed. Cela a du sens pour moi!
cuonglm