concernant sed -e portable… db ou! b?

12

Dans cette édition, Stéphane Chazelas POSIXifie (encore) ma sedmise en forme en insérant une -ecoupure xpression et une autre -einstruction xpression. Maintenant, je pourrais simplement lui demander pourquoi dans les commentaires, je suppose, mais c'est déjà la révision numéro 18 de cette réponse et presque toutes les précédentes étaient déjà grâce à des cadeaux similaires (si vous pouvez voir les commentaires supprimés, vous saurez quoi Je veux dire) . En outre, je pense que je suis assez proche pour comprendre pourquoi formuler cela d'une manière qui pourrait être plus généralement utile. Alors, j'espère ...

Je préfère généralement garder mes sed -expressions totales à un si je le peux, mais j'ai également une plus grande préférence pour me conformer aux spécifications aussi près que possible, surtout lorsque la différence ne dépasse pas a <space>et an -e. Mais je ne peux pas faire ça si je ne comprends pas pourquoi je devrais. Voici un bref aperçu de l'état actuel de ma compréhension:

  • la ' -e 'coupure peut remplacer de manière portative une coupure de sedscript \newline dans une seddéclaration de ligne de commande ... Je suis certes floue sur la raison

  • l'accolade fermante dans une sed {fonction }doit être précédée d'une \ncoupure de ligne électronique comme indiqué ici:

    • Le <right-brace>doit être précédé d'un <newline>et peut être précédé ou suivi de <blank>caractères.
  • une \npause ewline est nécessaire de la même suite à l'utilisation ... a, b, c, i, r, t, wou :.

Mais je ne comprends pas clairement comment la définition de la {fonction se }rapporte à l' !opérateur non. La seule mention que je trouve de l'opérateur de négation dans les états de spécification:

  • 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.

Est-ce à dire que l'utilisation d'un a !implique des {accolades }? Qu'en est-il des $!commandes - devraient-elles également être séparées par des ' -e 'pauses? Est-ce ce qui a été abordé lorsque Stéphane a récemment POSIXifié ma réponse?

Je pense que c'est soit l' !opérateur de négation, soit la bdéclaration de ranch qu'il aborde dans son édition - ou peut-être les deux à la fois - mais je ne sais pas et je voudrais le faire. Si ce n'est que la bdéclaration du ranch, alors je crois qu'un dferait à sa place et éliminerait le besoin de faire une ' -e 'pause, mais je préfère être certain avant de risquer une réponse POSIXifiée trois fois . Pouvez-vous m'aider?

Je l'ai risqué après tout , mais pas avec une grande certitude ...

mikeserv
la source
Avec b;n;:b, vous vous connectez au label appelé ";n;:b"historique et POSIX seds (et GNU sed ne l'est pas à cet égard).
Stéphane Chazelas
@ StéphaneChazelas - J'ai compris :- vous avez conduit cette maison il y a des mois. Mais je ne comprends pas vraiment pourquoi la deuxième sedcommande a été similaire POSIXified .
mikeserv
1
En tout cas, la spécification POSIX pour sedest très peu claire pour moi. J'ai demandé des clarifications à quelques reprises dans le passé, mais je ne pense pas que cela ait été mis à jour en conséquence. Un bon test consiste à essayer avec le toolchest d'héritage (Solaris, dérivé de l'original et sur lequel la spécification POSIX est largement basée).
Stéphane Chazelas
1
@syntaxerror - je ne crois pas du tout que ce soit le cas. si vous lisez la spécification, vous constaterez que les s///substitutions doivent accepter le chaînage avec un ; . cela devient flou autour des commandes qui doivent être délimitées par un retour à la ligne et comment -epeuvent y rester dans ce cas - du moins c'est le cas pour moi. ive encore à tomber sur un sedqui ne les interprète pas de manière assez interchangeable.
mikeserv
1
@syntaxerror - J'aime ça, mais vous devez savoir que vous n'avez pas besoin du ;avant une nouvelle ligne - une nouvelle ligne est très bien. Honnêtement, vous pouvez vous passer -eentièrement de et de tout et simplement écrire un fichier comme #!/bin/sedavec chaque commande sur une nouvelle ligne - ou ceux qui ne nécessitent pas de tels délimiteurs à la place délimités par ;. Ceux qui ne nécessitent habituellement les sauts de ligne sont ceux qui prennent entrée arbitraires - :noms d'étiquettes et les commandes qui les réfèrent à des bou tou la fermeture }Curlies pour les fonctions, ou read et write qui prennent args de nom de fichier. Ils doivent tous être portés de manière portative \n.
mikeserv

Réponses:

4

Il est donc grand temps que cette question ait une réponse, et, bien que j'aie fini par comprendre intuitivement comment le faire correctement dans presque tous les cas il y a quelque temps, je n'ai que très récemment réussi à concrétiser cette compréhension avec le texte de la norme. . En fait, il y est dit assez simplement - je l'ai simplement bêtement négligé plusieurs fois, je suppose.

Les parties pertinentes du texte se trouvent toutes sous le titre ...

  • Modification des commandes danssed :

    • Le texte de l' argument doit comprendre une ou plusieurs lignes. Chaque ligne \nélectronique intégrée dans le texte doit être précédée d'une \barre oblique inverse. Les autres barres obliques inverses dans le texte doivent être supprimées et le caractère suivant doit être traité littéralement.

    • Les verbes de commande ret w, et l' windicateur de la scommande, prennent un paramètre rfile (ou wfile ) facultatif , séparé de la lettre verbale ou de l'indicateur de commande par un ou plusieurs <blank>s; les implémentations peuvent permettre une séparation nulle comme extension.

    • Commande verbes autres que {, a, b, c, i, r, t, w, :et #peut être suivi d'un ;point - virgule, en option <blank>s, et un autre verbe de commande. Cependant, lorsque le sverbe de commande est utilisé avec l' windicateur, le suivre avec une autre commande de cette manière produit des résultats non définis.

...dans...

  • Options: plusieurs options -eet -fpeuvent être spécifiées. Toutes les commandes doivent être ajoutées au script dans l'ordre spécifié, quelle que soit leur origine.

    • -e script - Ajoutez les commandes d'édition spécifiées par l' argument option de script à la fin du script des commandes d'édition. L' argument-option de script doit avoir les mêmes propriétés que l' opérande de script , décrites dans la section OPERANDS .

    • -f script_file - Ajoutez les commandes d'édition dans le fichier script_file à la fin du script.

Et enfin ...

  • Opérandes:

    • script - Une chaîne à utiliser comme script d'édition des commandes. L'application ne doit pas présenter un script qui viole les restrictions d'un fichier texte, sauf que le caractère final n'a pas besoin d'être une ligne \nélectronique.

Donc, quand vous le prenez tout à fait, il est logique que toute commande qui est facultativement suivie d'un paramètre arbitraire sans délimiteur prédéfini (par opposition à s d sub d repl d flagpar exemple) devrait délimiter au niveau d'une ligne \nélectronique non échappée.

On peut soutenir que le ; est un délimiteur prédéfini, mais dans ce cas, l'utilisation de ;pour l'une des [aic]commandes nécessiterait qu'un analyseur distinct soit inclus dans la mise en œuvre spécifiquement pour ces trois commandes - distinct, c'est-à-dire de l'analyseur utilisé pour [:brw], par exemple. Sinon, l'implémentation devrait exiger que la barre oblique inverse soit ; également échappée dans le paramètre text et cela ne fera que se compliquer à partir de là.

Si je devais écrire un sedque je désirais être à la fois compatible et efficace, alors je ne serais pas écrire un tel analyseur séparé, je pense - sauf que peut - être [aic]devrait gen une erreur de syntaxe si pas immédiatement suivi d'un \newline. Mais c'est un problème de tokenisation simple - le cas du délimiteur de fin est généralement le plus problématique. Je voudrais simplement l'écrire ainsi:

sed -e w\ file\\ -e one -e '...;and more commands'

...et...

sed -e a\\ -e appended\\ -e text -e '...;and more commands'

... se comporterait de manière très similaire, dans la mesure où le premier créerait et écrirait dans un fichier nommé:

file
one

... et le second ajouterait un bloc de texte à la ligne actuelle sur la sortie comme ...

appended
text

... car les deux partageraient le même code d'analyse pour le paramètre.

Et en ce qui concerne la question { ... }et $!- eh bien, j'étais loin là-bas. Une seule commande précédée d'une adresse n'est pas une fonction mais plutôt une commande adressée. Presque toutes les commandes - y compris la { définition de fonction } sont spécifiées pour accepter /one/ou /one/,/two/adresser - à l'exception de la définition de #commentaire et d' :étiquette . Et une adresse peut être soit un numéro de ligne ou un express régulier et peut être annulée avec !. Donc tout ...

$!d
/address/s/ub/stitution/
5!y/d/c/

... peut être suivi par une ;et plusieurs commandes selon la norme, mais si plusieurs commandes sont requises pour une seule adresse, et que cette adresse ne doit pas être réévaluée après l'exécution de chaque commande, alors une {fonction }doit être utilisée comme:

/address/{ s//replace addressed pattern/
           s/do other conditional/substitutions/
           s/in the same context/without/
           s/reevaluating/address/
}

... où {ne peut pas être suivie sur la même ligne par une fermeture }et qu'une fermeture }ne peut se produire qu'en début de ligne. Mais si une commande contenue ne doit pas être suivie d'une ligne \nélectronique, elle n'a pas besoin non plus d'être dans la fonction. Ainsi, toutes les s///substitutions ci-dessus - et même l' }accolade de fermeture , peuvent être portées par des ;points-virgules et d'autres commandes.

Je continue de parler des \ndélimiteurs ewline mais la question concerne plutôt les -einstructions xpression, je sais. Mais les deux sont vraiment une seule et même chose, et la relation clé est qu'un script peut être soit un argument de ligne de commande littéral, soit un fichier avec l'un des deux -[ef], et que les deux sont interprétés comme des fichiers texte (qui sont spécifiés pour se terminer par un \newline) mais aucun ne doit en fait se terminer par une \newline. Par cela, je peux raisonnablement (j'espère) déduire qu'un \0NULargument délimité implique une ligne \nterminale, et comme tous les arguments d'invocation obtiennent au moins) un \0NULdélimiteur de toute façon, alors l'un ou l'autre devrait fonctionner correctement.

En fait, dans la pratique, dans tous les cas sauf un où la norme spécifie un \retour à la ligne avec une barre oblique inversée qui devrait être requis, j'ai trouvé de façon portable ...

sed -e ... -e '...\' -e '...'

... pour travailler aussi bien. Et dans tous les cas - encore une fois, dans la pratique - où une ligne \nélectronique non échappée devrait être requise ...

sed -e '...' -e '...'

... a aussi fonctionné pour moi. La seule exception que je mentionne ci-dessus est ...

sed -e 's/.../...\' -e '.../'

... qui ne fonctionne pour aucune implémentation dans aucun de mes tests. Je suis assez sûr que cela revient à l' exigence de fichier texte et au fait que cela s/// vient avec un délimiteur et donc il n'y a aucune raison qu'une seule instruction devrait couvrir des \0NULarguments délimités.

Donc, en conclusion, voici un bref aperçu des méthodes portables pour écrire plusieurs types de sedcommandes:

Pour n'importe lequel de [aic]:

...commands;[aic]\
text embedded newline\
delimiting newline
...more;commands...

...ou...

sed -e '...commands;[aic]\' -e 'text embedded newline\' -e 'delimiting newline' -e '.;.;.'

Pour tous les [:rwtb]cas où le paramètre est facultatif (pour tous sauf :) mais la ligne de délimitation \nne l'est pas . Notez que je n'ai jamais eu de raison d'essayer plusieurs paramètres d' étiquette de ligne comme cela serait utilisé avec [:tb], mais que writer / lire rsur plusieurs lignes dans les paramètres du fichier [rw] est généralement accepté sans question par seds J'ai testé tant que la ligne \nélectronique intégrée est échappé avec une \barre oblique inverse. Pourtant, la norme ne spécifie pas directement que les paramètres d' étiquette et de fichier [rw] doivent être analysés de manière identique au texteparamètres et ne fait aucune mention de \nlignes électroniques concernant les deux premiers sauf s'il les délimite.

...commands;[:trwb] parameter
...more;commands...

...ou...

sed -e '[:trwb] parameter' -e '...'

... où ce qui <space>précède est facultatif [:tb].

Enfin...

...;address[!]{ ...function;commands...
};...more;commands....

...ou...

sed -e '...;address[!]{ ...function;commands...' -e '};...more;commands...'

... où l'une des commandes susmentionnées (à l'exception de :) accepte également au moins une adresse et qui peut être soit une /expression rationnelle /soit un numéro de ligne et peut être annulée !, mais si plusieurs commandes sont nécessaires pour une seule évaluation de l' adresse, alors {des }accolades délimitant le contexte de la fonction doivent être utilisées. Une fonction peut contenir même plusieurs \ncommandes délimitées par ligne électronique, mais chacune doit être délimitée entre les accolades comme il en serait autrement.

Et voilà comment écrire des sedscripts portables .

mikeserv
la source
2
Pourquoi n'acceptez-vous pas votre propre réponse?
Philippos