sed regex: вариант группового повтора? - PullRequest
0 голосов
/ 07 февраля 2019

У меня есть текстовый ввод с несколькими группами строк.Каждая группа отделена пустой строкой (\ n \ n).Я обрабатываю с помощью sed, но я открыт для альтернатив.

Я использую эту конструкцию для возможности обрабатывать все строки одновременно:

# if the first line copy the pattern to the hold buffer
1h
# if not the first line then append the pattern to the hold buffer
1!H
# if the last line then ...
$ {
  # copy from the hold to the pattern buffer
  g

  ... here are my regex lines.

  # print
  p
}

Мой целевой выводдля каждой группы - каждая строка, но первая префиксная с содержимым первой строки, разделенной пробелом.

Поскольку мой текущий ввод имел только группы из 2, 3 и 6 строк, я «жестко закодировал» его вот так:

2 строки: s/\n\n\([^\n]\+\)\n\([^\n]\+\)\n\n/\n\n\1 \2\n\n/g

3 строки: s/\n\n\([^\n]\+\)\n\([^\n]\+\)\n\([^\n]\+\)\n\n/\n\n\1 \2\n\n\1 \3\n\n/g

6 строк: s/\n\n\([^\n]\+\)\n\([^\n]\+\)\n\([^\n]\+\)\n\([^\n]\+\)\n\([^\n]\+\)\n\([^\n]\+\)\n\n/\n\n\1 \2\n\n\1 \3\n\n\1 \4\n\n\1 \5\n\n\1 \6\n\n/g

(у меня есть каждая из этих строк регулярных выражений дважды, поскольку может понадобиться конец \ n \ n одной группы, и он недоступен для сопоставления с началом следующей группы)

Я ищу общий способ, который работает для групп любого размера от 2 до n строк.У кого-нибудь есть идеи на этот счет?

ОБНОВЛЕНИЕ: , поскольку @Benjamin W. запросил пример ввода / вывода:

Реальная проблема, которую я пытаюсь решить, заключается вдинамически генерировать строку заголовка csv для демона регистрации температуры, полученного из данных sensors -u.(потому что порядок вывода при изменении цикла моего ноутбука меняется)

С помощью sed было довольно легко получить исходный вывод программ:

jc42-i2c-0-1a SMBus I801 adapter at f040
temp1

asus-isa-0000 ISA adapter
cpu_fan
temp1

acpitz-acpi-0 ACPI interface
temp1

jc42-i2c-0-18 SMBus I801 adapter at f040
temp1

coretemp-isa-0000 ISA adapter
Package id 0
Core 0
Core 1
Core 2
Core 3

Заменить регулярное выражение 3 sedстроки, которые я упомянул выше, позволяют мне преобразовать это в это:

jc42-i2c-0-1a SMBus I801 adapter at f040 temp1
asus-isa-0000 ISA adapter cpu_fan
asus-isa-0000 ISA adapter temp1
acpitz-acpi-0 ACPI interface temp1
jc42-i2c-0-18 SMBus I801 adapter at f040 temp1
coretemp-isa-0000 ISA adapter Package id 0
coretemp-isa-0000 ISA adapter Core 0
coretemp-isa-0000 ISA adapter Core 1
coretemp-isa-0000 ISA adapter Core 2
coretemp-isa-0000 ISA adapter Core 3

Но это, конечно, будет работать только на машинах с адаптерами, которые имеют 1, 2 или 5 значений каждая.

ОБНОВЛЕНИЕ 2019-02-11:

Поэтому после того, как я получил два ответа, предлагающих общие решения, я снова посмотрел на эту проблему и немного упростил весь сценарий регистрации температуры:

echo -n "timestamp"
sensors -u | # -u gives Raw output, suitable for easier post-processing
grep --invert-match '  ' | # remove all lines containing values, leaving only headers
sed -n 'H; ${x; s/\nAdapter: / /g; p}' | # join headers spanning two lines together. For syntax see: https://unix.stackexchange.com/questions/163428/replace-a-string-containing-newline-characters & http://www.grymoire.com/Unix/Sed.html#uh-55
sed 'N;/\n$/d;s/\(.*\)\n\(.*\):/\1 \2\n\1/;P;$d;D' | # join the headers header with each sub-header, see: https://stackoverflow.com/questions/54576948/sed-regex-group-repeat-option
tr '\n' ';' | sed 's/.$//' # join finished headers together in a single line sepearted by ; & remove the trailing ;
echo ""

while true
do
    ts=`date +"%Y-%m-%d %H:%M:%S"`
    echo -n "$ts;"
    sensors -u | grep --invert-match '_max\|_crit\|_min' | # remove min max crit values which represent config, not state.
    grep '\.' | # remove all non value lines left (headers & empty lines seperating blocks
    sed 's/  .*: //g' | # remove value names, leaving only the values themselfs
    sed 's/\.000//g' | # remove empty decimals
    tr '\n' ';' | sed 's/.$//' # join finished values together in a single line sepearted by ; & remove the trailing ;
    sleep 1
    echo ""
done

Ответы [ 2 ]

0 голосов
/ 07 февраля 2019

Это работает как решение awk:

awk 'BEGIN {RS="\n\n"; FS="\n"} {for (i = 2; i <= NF; i++) print $1,$i}' file
  • определяет "\ n \ n" как разделитель записей (RS)
  • определяет "\ n" как разделитель полей (FS)
  • для каждого поля в каждой записи от второй до последней (NF): выведите первое поле ($ 1) и текущее поле ($ i), объединенные OFS, инициированные ","
0 голосов
/ 07 февраля 2019

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

sed 'N;/\n$/d;s/\(.*\)\n\(.*\)/\1 \2\n\1/;P;$d;D' file

Добавить следующую строку к текущей строке.

Если добавленная строка пуста, т. Е. \n$ обозначает пустуюline, полностью удалите пространство шаблона и возобновите его, как если бы ни одна строка не использовалась.

В противном случае обе строки в пространстве шаблона не являются пустыми, поэтому преобразуйте две строки в одну, а затем добавьте первуюстрока к результату.

Вывести первую строку в пространстве образца.

Если это последняя строка файла, удалить пространство образца.

Удалитьпервая из строк в пространстве шаблона.

Repeat.

NB. D удаляет первую строку в пространстве шаблона и неявно заменяет пространство шаблона следующей строкой, еслиПространство шаблона не пустое.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...