Удалить новую строку только для конкретного блока - PullRequest
1 голос
/ 25 апреля 2019

У меня есть файл yaml, который выглядит следующим образом:

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

Я пытаюсь добиться выравнивания всего блока, окруженного {} и [], так чтовывод в конце выглядит следующим образом:

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

Пока мне удалось сделать следующее:

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

Но это выравнивает весь контент yaml, просто удаляя все новыелинии.

Может ли кто-нибудь мне помочь?

Спасибо.

Примечание: ... обозначает некоторый контент, который в этом контексте не так важен.Но это определенно должно быть сохранено в конце.

Примечание 2: Отступы для внутреннего ({, [) и внешнего (}, ]) не определены и могутна самом деле меняются.Содержание yaml, которое я разместил, было просто примером.Содержание также может выглядеть следующим образом. Пожалуйста, взгляните на левую сторону, если на правой стороне указан желаемый результат: https://jsfiddle.net/u7wbxn8d/2

Примечание 3: Спасибо @potong, вот краткое изложение моеговопрос:

Решение должно собирать строки для тегов, которые начинаются в начале строки и сопровождаются :, сохраняя при этом исходные отступы.

(См. Пример в Примечании 2.)

Ответы [ 4 ]

1 голос
/ 25 апреля 2019

Вы не должны пытаться использовать линейно-ориентированные инструменты, такие как sed, чтобы попытаться изменить что-то настолько сложное, как YAML.Если в какой-то момент ваш входной сигнал изменится, но все еще будет действительным YAML, его сопоставление с образцом на основе регулярных выражений, скорее всего, сломается.

Вы можете легко достичь того, чего хотите, используя синтаксический анализатор YAML, например, мой ruamel.yaml для Python,Предполагая, что ваш test.yaml изменился на действительный YAML, заменив недействительный {...} на {answer: 42}

И этот flatten.py:

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)

работает:

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

дает out.yaml:

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

Вам необходимо предоставить специальный Представитель, так как null обычно представляется как пустой скаляр (см. this подробности ответа)

0 голосов
/ 26 апреля 2019

пробовал на gnu bash и sed:
вставлял собственное содержимое

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

пробовал на gnu awk:

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
0 голосов
/ 25 апреля 2019

Это может сработать для вас (GNU sed):

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

Это решение собирает строки для тегов, которые начинаются в начале строки и сопровождаются :.

0 голосов
/ 25 апреля 2019

Ваш подход добавления линий для блока в пространство образца возможен; мы просто должны сделать это только для блока, т.е. е. от строки, заканчивающейся [ или { до строки, начинающейся с ] или }:

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

Отступы как для внутреннего ({, [), так и для внешнего (}, ]) не определены и могут фактически отличаться.

Из вашей скрипки я понимаю, что отступ закрывающего ] или } равен отступу строки с открывающим [ или {. Основываясь на этом, это скрипт awk, который работает аналогично скрипту sed, но дополнительно обрабатывает отступы путем поиска одинаково отступающего конца блока:

#!/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

Я получаю эту ошибку при запуске вашего скрипта ./awk.sh: line 2: syntax error at or near ,

У вас есть версия awk, которая не поддерживает третий аргумент match(). Здесь вы можете заменить match($0, /( *)/, space) на match($0, /( *)/); space[1] = substr($0, RSTART, RLENGTH).

если закрывающая фигурная / квадратная скобка не начинается с новой строки, ваш скрипт выполняется вечно и не завершается.

Если квадратные скобки размещены не так регулярно, как предполагает приведенный выше сценарий, требуется другой подход. Следующий скрипт просто считает уровень вложенности скобок, пока он не достигнет нуля; кроме того, он проверяет конец файла, чтобы он не работал вечно, если файл содержит незамкнутый блок.

/[[{]$/ { 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...