LINUX.ORG.RU

Парсинг вывода iostat

 ,


0

2

Доброго времени суток

Есть идеи, как можно распарсить вот такой норкоманский кусок г вывод iostat?

http://pastebin.com/mDA3VJxV

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

З.Ы. Если интересно, это кусок вывода «iostat -aRDTl 2 2» на vios'е

★★★★★

Я не знаю что такое vios, все что доступно в iostat можно получить через /proc без вызова лишних execve.

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

+

открываешь сорцы iostat, смотришь откуда и как формируется вывод: открытая система же, ну

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

а какой результат нужен?

Разбить вывод на отдельные колонки, и выделить заголовки. Чтобы например заполнить таблицу

Т.е. например строка «fcs2/22:25:20», колонка «xfers/bps», значение - «131.1k»

По вертикали я текст разобью. выделю отдельные блоки Adapter, Disk, Vadapter и т.д. отделить строки с заголовком от строк с данными тоже не проблема.

Но как заголовок побить на столбцы? он же мать его за ногу, human readable в особо извращённой форме.

Т.е. как понять, что для «fcs2/22:25:20» значение «131.1k» относится именно к колонке «xfers/bps» ? Или, ещё хуже, в строке «hdisk72_157-uarc01/22:25:20» поле «0.0» относится к колонке «read/avg serv»

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

Можно написать функцию которая бы отдельно обрабатывала столбцы функциями типа pos substr

Если в вашем тексте есть логика расположения то парсинг возможен

ism ★★★
()
Ответ на: комментарий от router

Там же можно по строкам с минусами ориентироваться? Строка выше - заголовок, ниже - подзаголовки и данные.

А можно, наверное, и по-взрослому, чем-то типа flex/bison или что больше нравится. Токенизатор только будет замороченый, нужно будет обрабатывать пробелы (хорошо хоть там табов не видно)

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

Хотя, именно в этом тексте вообще просто - заголовки начинаются с заглавной буквы, подзаголовки с пробелов, данные со строчной

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

Пришлось как-то писать парсер идиотских логов, состоящих из последовательного выхлопа nfsstat, ps + прочей диагностики. При решении в лоб задача, в принципе, несложная, но кропотливая и муторная. Top-down approach решает: мысленно разбиваем текст на однородные куски всё уменьшающейся сложности и комбинируем парсеры.

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

Делалось всё на Haskell&Attoparsec&pipes.

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

Top-down approach решает: мысленно разбиваем текст на однородные куски всё уменьшающейся сложности и комбинируем парсеры.

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

Тут проблема в том, как выделить столбцы в однородном блоке

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

Не вижу проблем. Достаточно распарсить текст в какую-нибудь подходящую структуру данных типа матрицы, и из неё уже извлекать какие угодно столбцы.

Proof of concept для твоего файла (парсит только один тип записей — что-то там про диск)

{-# LANGUAGE TupleSections, OverloadedStrings #-}

import Prelude hiding (takeWhile)
import qualified Data.ByteString as B
import Data.Attoparsec.ByteString
import Data.Attoparsec.ByteString.Char8 hiding (satisfy, skipWhile, takeWhile1)
import Control.Arrow
import Control.Applicative
import Data.Word

type IOStat = (B.ByteString, [(B.ByteString, [Int])])

table :: ([c] -> a) -- ^ Postprocess
      -> Parser b   -- ^ Header
      -> Parser c   -- ^ Element
      -> Parser (b, [a])
table f h c = (,) <$> (h <?> "Header")
                  <*> (map f <$> many row)
  where
    row = (skipSpace <?> "Leading space") *>
          ((c <?> "ElemParser") `sepBy1` many1 space)
          <* (skipSpace <* endOfLine <|> endOfInput)

disks :: Parser IOStat
disks = table f header something
  where
    header = "Disks:" *> dropLines 4 *> pure "Disks"
    f (a:t) = (a, map parseInt t)

dropLines :: Word -> Parser ()
dropLines 0 = pure ()
dropLines n = skipWhile (not . isEndOfLine) *> endOfLine *> dropLines (n - 1)

parseInt :: B.ByteString -> Int
parseInt s = case parseOnly (p <* endOfInput) s of
               Left  _ -> error $ "parseInt: " ++ show s
               Right i -> round i
  where
    p = (*) <$> double <*> option 1 e
    e =   "K" *> pure (10^3)
      <|> "M" *> pure (10^6)
      <|> "G" *> pure (10^9)

something :: Parser B.ByteString
something = takeWhile1 $ not . isSpace_w8

parser :: Parser [IOStat]
parser = many1 (disks <?> "Disks")

main :: IO ()
main = print =<< parseOnly parser <$> B.getContents

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

P.S. Вывод:

[("Disks",[("hdisk0",[0,131100,1,0,131100,0,0,0,0,0,0,1,2,1,3,0,0,0,0,0,0,0])])]

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

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

Проблема выделить столбцы

Ну, «визуальные» выделить несложно. Я поморщил моск и написал такой вот «парсер» (без генераторов, на С вручную) : http://pastebin.com/K19Bqs2a

Выглядит конечно слегка адово, но суть простая: делаем две карты, карту заголовков и подзаголовков. Строки подзаголовков склеиваем. Каждому знакоместу (вертикальной полосе) ставим в соответствие заголовок и подзаголовок (или NULL если в строке с минусами пропуск). Когда доходим до разбора данных, берем значение, вычисляем его центр (номер знакоместа, расстояние от левого края) и смотрим что там в картах.

Если текст «поедет», то такое не сработает, конечно. Но тогда можно не морочиться, считать по пробелам, как уже посоветовали

Deleted
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.