LINUX.ORG.RU
ФорумTalks

Простой для парсинга ввод/вывод.

 , ,


0

2

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

Ответ на: комментарий от alozovskoy

Пример можно.
Вот такой, хоть и устаревший, но наглядный.
Команда ifconfig lo

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:1065524442 errors:0 dropped:0 overruns:0 frame:0
          TX packets:1065524442 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:492340424760 (458.5 GiB)  TX bytes:492340424760 (458.5 GiB)
Не будем рассматривать в данном случае то, что есть более удобная команда ip. Допустим нет.
Вывод ifconfig вполне можно распарсить, однако эти же данные сама команда могла бы выдать в ещё более удобном для парсинга виде, например в виде name=value на строку, или через другой разделитель, а так же перед этим могла бы идентифицировать в каком формате выводятся данные.
Для другого типа вывода программ, которые выводят данные в «табличном» виде, тоесть это строки с кучей полей, отделённых разными способами, так же можно предусмотреть специальный формат. Примеров можно ещё много привести, но для начала думаю хватит.

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

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

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

Повторюсь, json в моде, да. Если брать человекочитабельный, можно взять, например, toml. Но со стандартами такое дело...

feofan ★★★★★
()

Потому что эволюция в этом направлении замерла. Народ разбежался, понабежал новый стал изобретать всякие шины и прочие ipc. Потом тоже самое стали изобретать поверх http. Поэтом опять разбежались и стали изобретать html5 и всякие ваши WIN.RT.

В общем обычная история, сделали что-то и перестали дальше думать, наступила амнизия и уже никто не знает зачем были сделаны даже пайпы:)

ixrws ★★★
()

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

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

можно придумать и всевозможные конвейерные конверторы форматов

Для IPC всё уже придумано задолго до нас, например ASN.1 (используется в частности в SSL, SNMP, LDAP, GSM, ... много где)

Оно, правда, не текстовое. Зато само-документирующееся, компактное и с кучей типов.

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

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

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

Не надо мнение или наблюдения xkcd принимать за истину в посл.инст. Это случается, только когда очередной формат ничем не лучше предыдущего и решает какую-то одну проблему, вместо всех, при этом добавляя еще пару. Если решить все проблемы разом, то он быстро подавит остальные и мудиться с ними будут только маргиналы, которые до сих пор хранят картинки в tga или pcx. Лёнечка это всем доказал, как бы мы его не ненавидели за то, что он делает с «нашим линуксом»(ц)(тм).

arturpub ★★
()

PowerShell ждет тебя, там даже парсить ничего не надо, по пайпам передаются сразу объекты .NET

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

Высоковероятно, что PowerShell меня так и не дождётся.

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

применив стандартизованный подход

А там и есть стандартизированный подход. Всё есть текст. А вот, как оно там дальше — шерифа уже не волнует. И это, в общем то, правильно.

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

которые позволяли бы внутри программы извлекать сразу из потока структуры данных

Ты про:

fread(buff, struct_size, num, fp);
? Ну или какая разница даже если размер динамичекий? В первой очереди узнаешь «скока вешать в граммах» потом зная сколько читать — читаешь. Это же обертка в 10-15 строк.

deep-purple ★★★★★
()

Началось всё однажды с обработки больших объёмов данных. Но на мысль об универсальном формате подтолкнула последняя задачка: знать в каком состоянии находятся BGP пиры из quagg'и
И пришлось написать скрипт, чтобы получить искомые данные.
Но в нём не обыграны все ситуации, так как врядли весь формат вывода можно найти в документации, придётся анализировать исходники. Единицы измерения тоже от балды, тут килобайты, там мегабайты, форсировать единицы нельзя, время тоже в своеобразном формате.
Высока вероятность сбоя работы скрипта при столкновении с необычной формой данных, тоже самое при изменении версии программы источника данных.
И кроме того, этот скрипт обыгрывает только одину из форм вывода от quagg'и.
Вывод: для повышения надёжности обмена данными (что уже само по себе звучит абсурдно в рамках автоматизации или программирования), единственный путь - модификация самой quagga, в сторону добавления более однозначного формата вывода данных.

#!/usr/bin/perl

#[root@xxx bin]# vtysh -c 'show ip bgp summary'
#BGP router identifier XXXXXXX, local AS number XXXXXXXX
#RIB entries 1040654, using 95 MiB of memory
#Peers 3, using 13 KiB of memory

#Neighbor        V    AS MsgRcvd MsgSent   TblVer  InQ OutQ Up/Down  State/PfxRcd
#XXXXXXXXX    4  XXXX  431228    1348        0    0    0 00:23:12   566105
#XXXXXXXXX    4  XXXX  908243  296712        0    0    0 08:30:58   566101
#XXXXXXXXX    4  XXXX       0       0        0    0    0 never    Idle (Admin)

#Total number of neighbors 3

use Data::Dumper;

use strict;

my $stream;

my $argc = $#ARGV+1;

if ( $argc > 0 ) {
	if ( -e $ARGV[0] && -r $ARGV[0] ) {
		open $stream, '<', $ARGV[0]
			or die "Can't open file $ARGV[0]";
	}
} else {
	$stream = *STDIN;
}

my %fields;
my $index = 0;

sub uptime {
	my $upline = $_[0];
	my $uptime;
	if ($upline =~ /(\d+)w(\d+)d(\d+)h/) {
		$uptime = sprintf("%.2f", ($1*7)+$2+($3/24));
	} elsif ($upline =~ /(\d+)d(\d+)h(\d+)m/) {
		$uptime = sprintf("%.2f", $1 + ($2/24) + ($3/1440));
	} elsif ($upline =~ /(\d+)\:(\d+)\:(\d+)/) {
		$uptime = sprintf("%.2f", ($1/24) + ($2/1440) + ($3/86400));
	} elsif ($upline =~ /never/ ) {
		$uptime = -1;
	}
	return $uptime;
}

sub state_prefixes {
	my $state_pref = $_[0];
	if ( $state_pref =~ /\d+/ ) {
		return ('UP', $state_pref);
	} else {
		return ('DOWN', 0);
	}
}

if ( defined $stream ) {
	while ( my $line = <$stream> ) {
		$index++;
		chomp $line;
		print $line."\n";
		if ( $index == 1
			 && $line =~ m/^BGP router identifier (.+), local AS number (\d+)$/ ) {
			$fields{ 'bgp_router_id' } = $1;
			$fields{ 'local_as_number' } = $2;
			next;
		}
		if ( $index == 2
			 && $line =~ m/^RIB entries (\d+), using (\d+) (\w+) of memory$/ ) {
			$fields{ 'rib_entries' } = $1;
			$fields{ 'rib_memory' } = $2;
			$fields{ 'rib_memory_unit' } = $3;
			next;
		}
		if ( $index == 3
			 && $line =~ m/^Peers (\d+), using (\d+) (\w+) of memory$/ ) {
			$fields{ 'bgp_peers' } = $1;
			$fields{ 'bgp_peers_memory' } = $2;
			$fields{ 'bgp_peers_memory_unit' } = $3;
			next;
		}
		if ( $index == 4 ) { next; }
		if ( $index == 5
			 && $line =~ m/^Neighbor\s+V\s+AS\s+MsgRcvd\s+MsgSent\s+TblVer\s+InQ\s+OutQ\s+Up\/Down\s+State\/PfxRcd$/ ) {
			next;
		}
		if ( $index >= 6 and $index <= ($fields{ 'bgp_peers' } + 5) ) {
			if ( $line =~ m/^(\d+\.\d+\.\d+\.\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+([\w\:]+|never)\s+(\d+|Idle|Idle \(Admin\))$/ ) {
				
				my $peer_id = $1;
				
				my ($state, $prefixes) = state_prefixes( $10 );

				$fields{ "peer_$peer_id" }->{ 'id' } = $peer_id;
				$fields{ "peer_$peer_id" }->{ 'version' } = $2;
				$fields{ "peer_$peer_id" }->{ 'as' } = $3;
				$fields{ "peer_$peer_id" }->{ 'messages_received' } = $4;
				$fields{ "peer_$peer_id" }->{ 'messages_sent' } = $5;
				$fields{ "peer_$peer_id" }->{ 'tbl_ver' } = $6;
				$fields{ "peer_$peer_id" }->{ 'in_q' } = $7;
				$fields{ "peer_$peer_id" }->{ 'out_q' } = $8;
				$fields{ "peer_$peer_id" }->{ 'uptime' } = uptime( $9 );
				$fields{ "peer_$peer_id" }->{ 'state' } = $state;
				$fields{ "peer_$peer_id" }->{ 'prefixes' } = $prefixes;
			}
			next;
		}
		if ( $index == ($fields{ 'bgp_peers' } + 6 ) ) { next; }
		if ( $index == ($fields{ 'bgp_peers' } + 7 )
			 && $line =~ m/^Total number of neighbors (\d+)$/ ) {
			$fields{ 'total_neighbors' } = $1;
			if ( $1 == $fields{ 'bgp_peers' } ) {
				print "Parsed OK\n";
			} else {
				print "Something goes wrong:\n Peers = $fields{ 'bgp_peers' }\n Neighbors = $1\n";
			}
			next;
		}
	}
	print Dumper( \%fields );
}

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

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

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

Если у какой-то какашки нет апи, то это и есть какашка. То что ты делаешь — костылики. Да, возможно они нужные не одному тебе. Но отсутствие апи — не ваш косяк, а разработчика какашки. Я бы писал разработчику, и чем больше было бы таких как я, тем быстрее бы стало запилено искаропки.

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

Я бы писал разработчику, и чем больше было бы таких как я, тем быстрее бы стало запилено искаропки.

И че, написал уже, жопа с ушами?

Siado ★★★★★
()

Многие умы думали над этой проблемой. В том числе разработчик lsblk.

В итоге никто не предложил ничего лучше, чем использовать специальную опцию для генерации JSON-вывода. А для парсинга json есть отдельные cmdline утилиты, just for lulz.

Мне эта идея конечно по нраву. Это лучше чем парсить вывод команд. Но заминка в том, что это не более чем костыль. Пока bash(ну хотя-бы zsh) не будет иметь схожий с Lua механизм таблиц, это всё будет точно таким-же насилованием строк, только уже чуть более структурированных.

Подумайте, как бы было удобно, если бы shell умел парсить JSON, и автоматом конвертить его в таблицы/массивы? А там дальше манипулируй, как хочешь. Но этого нет даже в zsh.

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

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

Чтобы сам терминал умел сжимать/разжимать этот формат в текстово-графическое представление, типа свернутого дерева, списка, стены текста со встроенными картинками и звуками, с субкомандами/колбэками, которые можно выполнить по клику, с доп.каналами к программе-продюсеру, в которые снова можно что-то пихать. С драг-дропом и прочими достижениями UI, чтобы менять этот контент. И не было бы извечной проблемы отладки сложного протокола и просмотра данных, ходящих по нему. Автоматизация? Не вопрос. Презентация? Опять не вопрос (xattr-теги для настройки отображения самих данных, например). Редактирование без спец.редактора? Йеп.

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

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

который умеет делать текст жирным, вау

Ты забыл еще расцветку и всякие возвраты каретки. Но сарказм твой в тему, да.

deep-purple ★★★★★
()
Ответ на: комментарий от arturpub

Есть порт powershell'а под онтопик, можешь его пользовать. К этому можно прикрутить всё, что ты описал.

А, да. «Всё есть текст» — ложь, которая убила юниксы.

x3al ★★★★★
()

У многих программ есть соответствующие ключи. Например, ls --help:

  -m                         выдавать список на всю ширину через запятую
  -Q, --quote-name           заключать имя файла в кавычки

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

Есть порт powershell'а под онтопик

Это БУЭ ещё большее, чем парсинг XML. Ибо оно на Mono.

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