1
votes

Supprimer la nouvelle ligne uniquement pour un bloc spécifique

J'ai un fichier yaml qui ressemble à ceci:

sed ':a;N;$!ba;s/\n\s*//g' test.yml

Ce que j'essaie de réaliser, c'est l'aplatissement de tout le bloc, entouré de {} et [] , de sorte que la sortie à la fin ressemble à ceci:

user: John Doe
  notes: {...}
tags: [...]

Voici ce que j'ai réussi à faire jusqu'à présent:

user: John Doe
  notes: {
    "a": null,
    "b": null,
    "c": {
      "title": "...",
      "notes": [
        {...}, 
        {...}
      ]
    }
  }
tags: [
  "tag1", 
  "tag2", 
  "tag3"
]

Mais cela aplatit tout le contenu yaml en supprimant simplement toutes les nouvelles lignes.

Quelqu'un pourrait-il m'aider?

Merci.

Remarque: ... représente un certain contenu, ce qui dans ce contexte n'est pas si important. Mais il faut absolument le conserver à la fin.

Note 2: L'indentation est à la fois pour l'intérieur ( {, [) et externe (} , ] ) n'est pas certain et peut en fait varier. Le contenu yaml que j'ai publié n'était qu'un exemple. Le contenu peut également ressembler à ceci, s'il vous plaît jeter un oeil sur le côté gauche est l'entrée sur le côté droit la sortie souhaitée: https://jsfiddle.net/u7wbxn8d/2

Note 3: Merci à @potong, voici le résumé de ma question:

Le solution doit rassembler des lignes pour les balises qui commencent au début d'une ligne et sont suivies de : , tout en conservant les indentations d'origine.

(Veuillez consulter l'exemple dans la note 2.)


2 commentaires

Vous ne voulez pas que { } et [ ] dans la sortie contiennent uniquement ... , n'est-ce pas?


Oui. Le contenu doit être préservé!


4 Réponses :


0
votes

Votre approche consistant à ajouter des lignes pour un bloc dans l'espace de motif est réalisable; nous devons juste faire cela uniquement pour un bloc, i. e. depuis une ligne se terminant par [ ou { jusqu'à une ligne commençant par ] ou } :

/[[{]$/ { level = 1 # bracket nesting level
          do { printf "%s", $0; if (!getline) exit 1; sub(/ */, "")
               level += gsub(/[[{]/, "&") - gsub(/[]}]/, "&")
             } while (level)    # search block end
        }
        { print }   # print simple line as well as last line of block

J'obtiens cette erreur lorsque j'exécute votre script ./awk.sh: ligne 2: erreur de syntaxe à ou près de,

Vous avez une version de awk qui ne prend pas en charge le troisième argument match () . Ici, vous pouvez remplacer le match ($ 0, / (*) /, espace) par match ($ 0, / (*) /); espace [1] = substr ($ 0, RSTART, RLENGTH) .

si un crochet fermant / carré ne commence pas à une nouvelle ligne, votre script s'exécute indéfiniment et ne se termine pas.

Si les crochets ne sont pas placés aussi régulièrement que le script ci-dessus le suppose, une autre approche est nécessaire. Le script suivant compte simplement le niveau d'imbrication des crochets jusqu'à ce qu'il atteigne zéro; en plus, il teste la fin du fichier afin de ne pas s'exécuter indéfiniment si le fichier contient un bloc non fermé.

#!/usr/bin/awk -f
/[[{]$/ { match($0, /( *)/, space)  # space[1] contains the indentation
          do { printf "%s", $0; getline; t = $0; sub(/ */, "") }
             while (!match(t, "^"space[1]"[]}]"))   # search block end
        }
        { print }   # print simple line as well as last line of block


11 commentaires

Wow, ça a l'air fantastique. Cela fonctionne presque comme je le souhaite. Mais, si le ] ou le } englobant a également une indentation, alors malheureusement cela ne fonctionne pas. :( Je sais que dans l'exemple yaml j'ai posté ceux qui les entourent n'ont pas d'indentation, mais cela devrait également fonctionner avec l'indentation. Merci!


@DenverNuggets - Si vous postez un exemple où cela ne fonctionne pas, je l'examinerais.


@DenverNuggets - Ou, pourriez-vous répondre à ceci: Les fermetures les plus externes ] ou } sont-elles toujours en retrait moins d'un certain montant, et les fermetures internes sont-elles toujours plus en retrait?


L'indentation est à la fois pour l'intérieur ( {, [) et externe (} , ] ) pas certain et peut en fait varier. Le contenu yaml que j'ai publié n'était qu'un exemple. Le contenu peut également ressembler à ceci, s'il vous plaît jeter un oeil sur le côté gauche est l'entrée sur le côté droit la sortie souhaitée: jsfiddle.net/u7wbxn8d/2


@DenverNuggets - J'ai ajouté une version qui devrait gérer les indentations.


Hey @Armali, merci beaucoup pour vos efforts! J'apprécie vraiment cela! Pour être clair: les crochets fermants / carrés peuvent avoir une indentation différente. Dans mon premier exemple, celui de fermeture n'avait pas d'indentation, plus tard j'ai dit que l'indentation des deux mught varie, mais cela ne veut pas dire que l'ouverture et la fermeture doivent être absolument identiques. Désolé pour le malentendu! Donc, mon problème est tout au sujet, dès qu'un crochet ouvrant ou carré est capturé, il devrait suivre le contenu tout en bas jusqu'à ce qu'il trouve l'équivalent de fermeture et aplatir tout le bloc en supprimant simplement la nouvelle ligne.


Notez également que j'obtiens cette erreur lorsque j'exécute votre script ./awk.sh: ligne 2: erreur de syntaxe à ou près de, . Apparemment, il y a un problème avec la première partie -> match ($ 0, / (*) /, arr)


@DenverNuggets - Votre jsfiddle a montré assez bien comment les indentations sont, donc le script awk peut fonctionner; J'ai ajouté un changement pour le laisser fonctionner également avec une ancienne version awk .


le correctif awk a fonctionné, merci beaucoup! Au fait, votre solution fonctionne bien, mais si un crochet fermant / carré ne commence pas à une nouvelle ligne, votre script s'exécute indéfiniment et ne se termine pas. Ce que je veux dire, c'est ici -> jsfiddle.net/98q06ecn . Quand je mets le ] de fermeture juste après "tag3" dans la même ligne, le script s'exécute pour toujours ...


Ce serait vraiment génial si vous pouviez suggérer une solution à ce problème, car les crochets fermants peuvent être simplement dans la même ligne et n'ont pas à commencer par une nouvelle ligne. Merci beaucoup, mec!


@DenverNuggets - J'ai ajouté une version qui gère les indentations irrégulières.



1
votes

Vous ne devriez pas essayer d'utiliser des outils orientés ligne comme sed pour essayer et changer quelque chose d'aussi complexe que YAML. Si à un moment donné votre entrée change, mais est toujours valide YAML, son modèle basé sur une expression régulière la correspondance est susceptible de casser.

Vous pouvez facilement obtenir ce que vous voulez en utilisant un analyseur YAML, par exemple ma ruamel.yaml pour Python. En supposant que votre test.yaml a été modifié pour être valide YAML en remplaçant le {...} non valide de {answer: 42}

Et ce flatten.py : p >

user: John Doe
notes: {"a": null, "b": null, "c": {"title": "...", "notes": [answer: 42, answer: 42]}}
tags: ["tag1", "tag2", "tag3"]

en cours d'exécution:

< test.yaml python flatten.py > out.yaml

donne un out.yaml:

import sys
import ruamel.yaml

class MyRepresenter(ruamel.yaml.representer.RoundTripRepresenter):
    def represent_none(self, data):
        return self.represent_scalar(u'tag:yaml.org,2002:null', u'null', style='')

MyRepresenter.add_representer(type(None),
                                     MyRepresenter.represent_none)

yaml = ruamel.yaml.YAML()
yaml.Representer = MyRepresenter
yaml.preserve_quotes = True
yaml.width = 4096  # line width before wrapping
data = yaml.load(sys.stdin)
yaml.dump(data, sys.stdout)

Vous devez fournir le représentant spécial car null est normalement représenté comme le scalaire vide (voir cette réponse pour détails)


1 commentaires

Merci pour cette suggestion. Il a l'air beau et propre! Malheureusement, je dois utiliser bash, donc je n'ai que sed et awk disponibles.



0
votes

Cela pourrait fonctionner pour vous (GNU sed):

sed ':a;N;/\n\S\+:/!s/\n\s*//;ta;P;D' file

Cette solution rassemble des lignes pour les balises qui commencent au début d'une ligne et sont suivies de : code >.


1 commentaires

Hé @potong, désolé pour la réponse tardive. Votre suggestion fonctionne réellement! Malheureusement uniquement pour les structures où il n'y a pas d'imbrication, c'est-à-dire pour mon tout premier exemple yaml. Pour celui-ci cependant cela ne fonctionne pas (veuillez vous référer à la section HTML): jsfiddle.net/u7wbxn8d/2



0
votes

essayé sur gnu bash et sed:
Mettez votre propre contenu

awk -vf=0 -vNOTE='notes' -vCONTENT='contents' '$0~/^NOTE/||f{f=1;print $0""CONTENT"}";while(getline&&$0!~/^\}/);f=0;next} $0~/^tags/||f{f=1;print $0""CONTENT"]";while(getline&&$0!~/^\]/);f=0;next}1' test.yml

essayé sur gnu awk:

$ CONTENT='contents'  
$ NOTE='notes'
sed -E "/^$NOTE/,/^\}/{/^notes/{s/.*/&$CONTENT\}/;b};d} ;/^tags/,/^\]/{/^tags/{s/.*/&$CONTENT\]/;b};d}" test.yml


1 commentaires

La touche `` notes` n'était qu'un exemple. Cela peut être autre chose. Ainsi, il ne devrait pas être codé en dur dans regex. Merci.