LINUX.ORG.RU

Разбор вывода команды и запись результатов в файл.

 


0

3

Хочу поинтересоваться у уважаемых присутствующих Гуру каким инструментом они порекомендуют решать вот такую задачу. Мне в голову несколько вариантов приходит, но затрудняюсь с выбором.

Есть команда которая читает параметры сигнала из радиомодема и показывает на экране в неудобно отформатированном виде.

qmicli -d /dev/cdc-wdm0 --nas-get-signal-strength

Не понимаю зачем было форматировать именно так,но что есть то есть. Ввыод такой:

[/dev/cdc-wdm0] Successfully got signal strength
Current:
        Network 'lte': '-73 dBm'
RSSI:
        Network 'lte': '-73 dBm'
ECIO:
        Network 'lte': '-2.5 dBm'
IO: '-106 dBm'
SINR (8): '9.0 dB'
RSRQ:
        Network 'lte': '-8 dB'
SNR:
        Network 'lte': '7.0 dB'
RSRP:
        Network 'lte': '-101 dBm'

Её можно запускать раз в несколько секунд и вот такое получать. Хочется писать это в одну строчку в CSV файл для целей последующего построения графиков например в программе grace. Чтобы в файле строчка выглядела так:

-73,-2.5,-106,9.0,-8,7.0,-101

Вобщем все цифры через запятую кроме повторяющей первой. Так как предполагается что это будет работать раз в несколько секунд то кручения многочисленных вложенных циклов хотелось бы избегать. Может вообще возможно всё это в одну длииинную строку загнать,а потом написать что-то типа форматной строки для сишного sscanf? Или еще какой-нибудь красивый способ есть?



Последнее исправление: watchcat382 (всего исправлений: 1)
Ответ на: комментарий от wandrien

Да чем угодно, что умнее чем grep и sed

Вот и я на это неявно намекал в своем посте :) Но ruby это как-то уж совсем экзотично - никогда с ним дела не имел и не знаю совсем. Хотя за возможный вариант спасибо.

watchcat382
() автор топика
Ответ на: комментарий от dataman

Можно натянуть сову

Еще одна сова,в смысле тоже генератор парсеров по описанию грамматики: https://imatix-legacy.github.io/libero/lrdoc.htm Я даже этим пользовался. Но написать правильную грамматику сложнее чем пример выше на awk.

watchcat382
() автор топика
Ответ на: комментарий от gagarin0

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

Я впечатлён!

watchcat382
() автор топика
Ответ на: комментарий от anonymous

Это YAML

Ну так и сделали бы вывод в виде имя:значение

SNR: 7.0 dB

Куда проще было бы разбирать. Нет,надо было многоэтажную конструкцию нагородить,а теперь мучайся с ней.

watchcat382
() автор топика
Ответ на: комментарий от wandrien

Извините,не понял аналогию. Чем плох однострочный вариант на sed,ну кроме того что очень мало кто способен такое написать и даже понять написанное не просто. Я просто не ожидал что sed может это одной командой разбирать,а не вызовом sed на каждую строку в цикле как написал бы я. Он же строчный редактор,следовательно в представлении большинства людей обрабатывает файл построчно.

watchcat382
() автор топика
Ответ на: комментарий от anonymous

«параметр»:«сеть»:«значение»

Сеть можно в начале указать

network: lte

Или если хочется всё-таки в каждом параметре то в одной строке именно так как вы написали

SNR: Network 'lte': '7.0 dB'

Зачем было вставлять столько переносов строк и затруднять жизнь тем кто захочет это разбирать - не знаю.

watchcat382
() автор топика
Ответ на: комментарий от gagarin0

Задумался - а не получится ли саму команду qmcli из awk вызывать в цикле чтобы поверх этого цикл на bash не делать и не перезапускать awk на каждую итерацию?

watchcat382
() автор топика
Ответ на: комментарий от watchcat382

а не получится ли саму команду qmcli из awk вызывать в цикле

так делать можно, но не нужно

расскажите полностью условия задачи

gagarin0
()
Последнее исправление: gagarin0 (всего исправлений: 1)
Ответ на: комментарий от watchcat382

питон. 1) малтилайн регулярка 2) разбивка по строкам в список, далее регулярка для разбора строк внешнего уровня, доступ к вложенной строке по индексу +1, далее регулярка. 3) автомат на свиче + дальше однострочная регулярка для вложенных строк

asdpm
()
Последнее исправление: asdpm (всего исправлений: 1)
Ответ на: комментарий от watchcat382

а не получится ли саму команду qmcli из awk вызывать в цикле

# cat test | awk 'match($0, /([-0-9.]+) dBm?/, m) {system("echo execute: qmcli " m[1])}'
execute: qmcli -73
execute: qmcli -73
execute: qmcli -2.5
execute: qmcli -106
execute: qmcli 9.0
execute: qmcli -8
execute: qmcli 7.0
execute: qmcli -101
iron ★★★★★
()

У меня крутится похожая штука для бесперебойника. Считая бинарник, общающийся с хардварью чёрным ящиком, я сделал защиту от изменения порядка выхлопа строк. Ниже файлы, может пригодится. Запрос запускается в цикле, а sh и awk в единственном экземпляре до ребута.

cat /usr/local/bin/upslog.sh

#!/bin/sh

# раньше оно работало в кроне вот так
# /usr/bin/upsc ippon-spp-2000@localhost 2> >( /usr/bin/grep -v 'Init SSL without certificate database' >&2) | /usr/local/bin/upslog.awk >> /var/log/ups_state.log
[ -s /var/log/ups_state.log ] || FIRSTRUN="-vFIRSTRUN=1"
while :; do
    sleep 60 &
    /usr/bin/upsc ippon-spp-2000@localhost
    wait
done |
    /usr/local/bin/upslog.awk $FIRSTRUN >> /var/log/ups_state.log

cat /usr/local/bin/upslog.awk

#!/bin/gawk -f
BEGIN {
        FS=":\\s+"
        split("\
TIME\n\
battery.charge\n\
battery.runtime\n\
battery.voltage\n\
battery.voltage.high\n\
battery.voltage.low\n\
input.frequency\n\
input.voltage\n\
input.voltage.fault\n\
output.voltage\n\
ups.load\n\
ups.status\
", datakeys, "\n")

        for(i in datakeys)
                data[datakeys[i]]=""
        if (FIRSTRUN)
        {
                printf("SEP=;\n");
                for(i in datakeys)
                        printf("%s;",datakeys[i])
                printf("\n")
        }
}
$1 in data {
        data[$1]=$2
}

/ups.vendorid/ {
        data["TIME"]=strftime("%FT%T%z")
        for(i in datakeys)
                printf("%s;",data[datakeys[i]])
        printf("\n")
        fflush()
}

cat /usr/local/lib/systemd/system/upslog.service

[Unit]
Description=Log ups status
After=nut-server.service
Requires=nut-server.service

[Service]
Type=simple
ExecStart=/usr/local/bin/upslog.sh

[Install]
WantedBy=default.target

Можно ещё запилить конфиг для logrotate, но мне было лень.

PS бонус - график

cat ~/bin/ups_state.gnuplot

#!/usr/bin/gnuplot

reset
set title "ИБП"
set time
set timefmt "%Y-%m-%dT%H:%M:%S+0300"
TZ=+3*60*60
set grid
set key autotitle columnhead 
set datafile separator ";"
set term wxt size 1200,800
set xdata time
set format x "%y-%m-%d %H:%M"
set xtics rotate by 30 right autofreq
set ylabel "вольт"
set y2label "минут"
set y2tics
set style data lines
SEESECS=36000
SEEFWD=1200
bind all "Close" "unset output ; exit gnuplot"
#while (1) {
#    if (exists("GPVAL_DATA_X_MAX")) {
#        if (GPVAL_DATA_X_MAX > SEESECS) {
#            set xrange[GPVAL_DATA_X_MAX-SEESECS:GPVAL_DATA_X_MAX+SEEFWD];
#        }
#    }
    set y2range[0:100]
    set xrange[time(0)+TZ-SEESECS:time(0)+TZ+SEEFWD];
    plot "/var/log/ups_state.log" \
           skip 1 using 1:8,\
        "" skip 1 using 1:10,\
        "" skip 1 using 1:11,\
        "" skip 1 using 1:4,\
        "" skip 1 using 1:($3/60) axis x1y2
    #pause 30;
    
#}
pause -1
legolegs ★★★★★
()
Ответ на: комментарий от gagarin0

расскажите полностью условия задачи

В первом посте всё полностью и написано - преобразовывать весьма извращенный вывод команды qmicli в строку числе,разделенных запятыми для записи в csv-файл. Можно конечно написать цикл на баше,который будет раз в несколько секунд перезапускать qmicli … | awk Но если команду для получения данных можно запускать из самого awk то почему бы так не сделать?

Другой вариант - запускать qmicli … | awk из crontab,но это тоже перезапуск awk каждый раз

watchcat382
() автор топика
Ответ на: комментарий от watchcat382

тоже перезапуск awk каждый раз

это дешево по накладным расходам

Но если команду для получения данных можно запускать из самого awk

а этот антипаттерн unixway, так делать не надо

gagarin0
()
Ответ на: комментарий от wandrien

Тем, что нечитабельно.

Согласен,такую команду для sed не то что написать,ее понять невозможно. Особенно учитывая что обычно sed работает со строками по одной,то есть без LF внутри. Я например вообще не знал что он может обрабатывать по несколько строк сразу.

watchcat382
() автор топика
Ответ на: комментарий от anonymous

Потому что YAML

Вот именно - один выпендрился,причем в документации нет даже намека на слово YAML,а теперь все остальные должны писать извращенные скрипты и команды чтобы это разбирать :(

watchcat382
() автор топика
Ответ на: комментарий от wandrien

Я тоже думал в сторону Питона. Так как qmicli вызывает функции из libqmi то по всей видимости можно вызывать их и из Питона, и форматировать полученные данные как удобно. Но я не настолько знаток Питона чтобы сходу разобраться как дергать из него сишную библиотеку. Поэтому решил ограничиться разбором вывода qmicli. Но ничего умнее вот такого на баше родить не смог:

output=$(qmicli -d /dev/cdc-wdm0 --nas-get-signal-strength) 
rssi=$(echo "$output" | awk -F"'" '/RSSI:/ {getline; print $4}' | tr -d ' ')

Это конечно можно допилить до работоспособного состояния и использовать,но не тогда, когда предполагается собирать данные раз в несколько секунд. Потому к общественности и обратился.

watchcat382
() автор топика
Ответ на: комментарий от iron

Предлагается вызывать qmicli отдельно для получения каждого параметра что ли? Не,это еще хуже чем один перезапуск awk для получения сразу всех параметров.

watchcat382
() автор топика
Ответ на: комментарий от gagarin0

тоже перезапуск awk каждый раз

это дешево по накладным расходам

Но не тогда, когда раз в несколько секунд. Делать надо красиво и оптимально. Вон коллега выше сделал для UPS. Буду пожалуй в его конструкции разбираться - у него awk не перезапускается каждый раз. Всего-то надо адаптировать предложенный выше скрипт awk для разбора qmicli для такого варианта его использования.

watchcat382
() автор топика
Ответ на: комментарий от watchcat382

У меня не установлен qmicli, но интернет говорит, что есть --nas-get-signal-info

# qmicli -d /dev/cdc-wdm0 --nas-get-signal-info
[/dev/cdc-wdm0] Successfully got signal info
LTE:
	RSSI: '-74 dBm'
	RSRQ: '-13 dB'
	RSRP: '-101 dBm'
	SNR: '2.4 dB'

Более «разобрчиво»?

anonymous
()
Ответ на: комментарий от anonymous

Более «разобрчиво»?

Так в том-то и дело что в этом случае сделали нормальный формат вывода,но забыли вывести параметры ECIO,IO,SINR. А там где они выводятся - формат вывода сделали дико неудобным.

watchcat382
() автор топика
Ответ на: комментарий от gagarin0

антипаттерн unixway, так делать не надо

антипаттерн - это каждые несколько секунд перезапускать целую связку достаточно толстых программ вместо одной. Вот представленный коллегой мониторинг для UPS сделан правильно, без лишних перезапусков. Хотя и используется bash+awk но они оба запущены всё время пока скрипт работает.

watchcat382
() автор топика
Ответ на: комментарий от watchcat382

Что мешает для удобства использовать парсер yaml для perl, python, ruby и тп.

И вообще, если формат не определен, то парсить вывод можно только (не)искусственным интеллектом.

ECIO,IO,SINR

Избыточные метрики, насколько я знаю, которые вычисляются по другим формулам из тех же самых исходных данных.

anonymous
()

Не рекомендую действовать методом «найти все числа». Потом формат вывода команды где-нить изменится, числа станут в другом порядке или другом наборе и у тебя начнут записываться ложные данные. Я бы составил строгий шаблон этого вывода и парсил через него.

То есть:

1) пропускаем «Current:\n'tNetwork 'lte': '»

2) парсим число float A1

3) пропускаем " dBm'\nRRSI:\n\tNetwork 'lte': '"

и т.д.

При несовпадении хотя бы одного символа в пропускаемом тексте или при ошибке парсинга числа (т.е. мы ждём что там число а там что-то другое) - сразу сигнализировать об ошибке - оповещать тебя чтобы ты смотрел что случилось и патчил парсер.

firkax ★★★★★
()
Последнее исправление: firkax (всего исправлений: 1)
Ответ на: комментарий от anonymous

использовать парсер yaml

Как-то очень уж из пушки по воробьям получается. Вместо решения простой задачи разбора нескольких строчек вывода - изучение этих парсеров и их особенностей. Вон выше решение для UPS сделано я считаю идеально для такой задачи сбора данных. Так что адаптирую под него скрипт на awk предложенный в начале обсуждения и буду пользоваться.

Избыточные метрики, насколько я знаю, которые вычисляются по другим формулам

Так ведь придется искать эти формулы и самому вычислять. А тут уже готовые есть,вычисляемые по правильным формулам.

watchcat382
() автор топика

Зачем жахать структурированные данные регулярками — чай, не 70е на дворе. Есть же специализированные утилитки для разбора всяких ямлов с джейсонами из командной строки.

$cat ~/example.yml  | yq '.. | select(tag == "!!str") | split(" ") | omit([1])' | yq '. as $item ireduce([]; $item + .) | omit([0])' -o csv

-73,-2.5,-106,9.0,-8,7.0,-101

Только первую строку из выхлопа qmicli убрать, и будет ямл.

Nervous ★★★★★
()
Последнее исправление: Nervous (всего исправлений: 1)
Ответ на: комментарий от anonymous

Попробовал в качестве эксперимента. Оказалось что там не просто вывод неудобного вида,так еще и табы вместо пробелов вставлены:( Перед словом «Network».

yaml.scanner.ScannerError: while scanning for the next token found character ‘\t’ that cannot start any token

watchcat382
() автор топика
Ответ на: комментарий от watchcat382
$ qmicli -d /dev/cdc-wdm0 --nas-get-signal-info | sed '1d;s/\t/  /g' | yq '.. | select(tag == "!!str") | split(" ") | omit([1])' | yq '. as $item ireduce([]; $item + .) | omit([0])' -o csv

gagarin0
()
Последнее исправление: gagarin0 (всего исправлений: 1)
Ответ на: комментарий от anonymous

А вот если в файлике куда вывод qmicli я записал поменять табы на пробелы - таки действительно работает.

{'Current': {"Network 'lte'": '-74 dBm'}, 'RSSI': {"Network 'lte'": '-74 dBm'}, 'ECIO': {"Network 'lte'": '-2.5 dBm'}, 'IO': '-106 dBm', 'SINR (8)': '9.0 dB', 'RSRQ': {"Network 'lte'": '-7 dB'}, 'SNR': {"Network 'lte'": '15.0 dB'}, 'RSRP': {"Network 'lte'": '-98 dBm'}}

Но не совсем так как надо бы. Значениями параметров получаются не числа, а строки с размерностью типа ‘15.0 dB’. Так что это надо дальше обрабатывать. Понятно что сделать можно но опять руками.

watchcat382
() автор топика
Ответ на: комментарий от wandrien

Тем, что нечитабельно.

Я нашел способ прочитать эту команду для sed. Идем вот сюда:

https://duckduckgo.com/?q=DuckDuckGo+AI+Chat&ia=chat&duckai=1

и просим искусственный интеллект:

" объясни что делает вот эта команда sed -rn «/dBm?‘/ {s/.*’(-?[0-9.]+).*/\1/g; H}; ${g; s/\n/,/g; s/^,//; p}» "

Как ни странно - объяснил внятно, подробно и бесплатно. Мне понравилось.

watchcat382
() автор топика
Ответ на: комментарий от gagarin0

И чем конвейер из трех вызовов sed | yq | yq лечше чем один вызов хоть того же sed хотя и с зубодробительной командой? Если уж всё равно конвейер каждый несколько секунд запускать то лучше бы ему быть как можно короче.

watchcat382
() автор топика