Portál AbcLinuxu, 3. května 2025 14:08
Nově oznámená verze Relačních rour v0.18 přináší řadu změn – podporu mnoha nových formátů, protokolů a transformací (ASN.1, INI, MIME, YAML, JSON, CBOR, HTML, QR, X11, XPath, XMLTable…). Nová je i podpora datových typů v CSV.
Umožňují pracovat jednotným způsobem s řadou formátů (ASN.1, INI, MIME, YAML, JSON, CBOR, HTML, XML) a vytěžit z nich informace podobným způsobem jako to dělá databázová funkce XMLTable s daty ve formátu XML. Můžeme tak vytáhnout zajímavé informace např. z X.509 certifikátů, e-mailových zpráv nebo (i nevalidních) webových stránek.
Můžeme se napojit na systém X11 a vypsat si otevřená okna, obrazovky či vstupní zařízení nebo zachytávat i generovat události (pohyby myši, stisky kláves).
Z obrázků můžeme načítat čárové a QR kódy a to včetně souřadnic kódu (na obrázku jich může být víc).
Filtry pro čtení a zápis CSV nově podporují datové typy (text, číslo, boolean) a to jak explicitně uložené v CSV, tak jejich autodetekci.
Filtry pro pro práci s INI soubory podporují řadu dialektů (formát INI není standardizovaný) včetně klasických unixových konfiguračních souborů typu klíč=hodnota nebo javovských .properties a MANIFEST.MF souborů.
Relational pipes jsou datový formát pro popis relačních dat + související nástroje, které slouží jako vstupní či výstupní filtry nebo transformace. Myšlenkově navazují na klasické unixové roury (skládání příkazů, každý program dělá jednu věc) a relační databáze.
Tiskni
Sdílej:
<polozky> <polozka> <jmeno>prvni</jmeno> <barva>cervena</barva> </polozka> <polozka> <jmeno>druha</jmeno> <barva>zelena</barva> </polozka> <polozka> <jmeno>treti</jmeno> <barva>modra</barva> </polozka> </polozky>naplnit pole:
jmeno[0]=prvni jmeno[1]=druha jmeno[2]=treti barva[0]=cervena barva[1]=zelena barva[2]=modraaktualne to delam pres spocitani polozek a pak rozdelit/parsovat v poctu cyklu pres grep,tac,cat,sed...
To by použít šlo. Pro zajímavost přikládám Makefile pro stažení toho XML z tvého komentáře:
all: položky.xml .PHONY: clean clean: rm -f diskuse.html rm -f položky.xml diskuse.html: wget -O $@ --xattr https://www.abclinuxu.cz/zpravicky/relational-pipes-v0.18/diskuse#8 položky.xml: diskuse.html cat $(<) \ | relpipe-in-htmltable \ --relation 'pre' \ --records '//pre[1]' \ --attribute 'pre' string '.' \ | relpipe-out-nullbyte | tr \\0 \\n \ > $@
To z HTML stránky extrahuje obsah prvního <pre/> elementu:
<polozky> <polozka> <jmeno>prvni</jmeno> <barva>cervena</barva> </polozka> <polozka> <jmeno>druha</jmeno> <barva>zelena</barva> </polozka> <polozka> <jmeno>treti</jmeno> <barva>modra</barva> </polozka> </polozky>
A potom to načtení XML a naplnění do proměnných:
#!/bin/bash make položky.xml read_nullbyte() { local IFS=; for v in "$@"; do export "$v"; read -r -d '' "$v"; done } jmeno=(); barva=(); while read_nullbyte jmeno barva; do jmeno+=("$jmeno"); barva+=("$barva"); done < <(cat položky.xml \ | relpipe-in-xmltable \ --relation 'položka' \ --records '/polozky/polozka' \ --attribute 'jméno' string 'jmeno' \ --attribute 'barva' string 'barva' \ | relpipe-out-nullbyte) for j in ${jmeno[@]}; do echo "jméno: $j"; done for b in ${barva[@]}; do echo "barva: $b"; done
Případně bez proměnných a rovnou nad každou položkou volat nějaký kód:
#!/bin/bash make položky.xml read_nullbyte() { local IFS=; for v in "$@"; do export "$v"; read -r -d '' "$v"; done } cat položky.xml \ | relpipe-in-xmltable \ --relation 'položka' \ --records '/polozky/polozka' \ --attribute 'jméno' string 'jmeno' \ --attribute 'barva' string 'barva' \ | relpipe-out-nullbyte \ | while read_nullbyte j b; do echo "jméno: $j"; echo "barva: $b"; done
Vypíše to:
make: „položky.xml“ je aktuální. jméno: prvni jméno: druha jméno: treti barva: cervena barva: zelena barva: modra
P.S. k té funkci read_nullbyte()
: ptal jsem se v konferenci Bashe, jestli by to šlo nějak lépe: Help-bash] Readig multiple null-separated values, ale asi nešlo. Přišlo by mi užitečné, kdyby to byla součást shellu resp. jeho funkce read
.
<polozky> <polozka> <jmeno>prvni</jmeno> <barva>cervena</barva> <jmeno>prvni</jmeno> <info> <vyska>100</vyska> <sirka>50</sirka> </info> </polozka> <polozka> <jmeno>druha</jmeno> <barva>zelena</barva> <info> <vyska>200</vyska> <sirka>60</sirka> </info> </polozka> <polozka> <jmeno>treti</jmeno> <barva>modra</barva> <info> <vyska>300</vyska> <sirka>70</sirka> </info> </polozka> </polozky>naplnit pole:
jmeno[0]=prvni jmeno[1]=druha jmeno[2]=treti barva[0]=cervena barva[1]=zelena barva[2]=modra vyska[0]=100 vyska[1]=200 vyska[2]=300 sirka[0]=50 sirka[1]=60 sirka[2]=70
Pořádnou dokumentaci teprve píšu, zatím jsou jen ty příklady a bash-completion skripty.
Do toho --attribute
se píše název (jak se to má jmenovat na výstupu), typ a XPath výraz – ve kterém se dá jednoduše odkázat na vnořený element (dá se toho tam dělat i mnohem víc, volat různé funkce atd.). Takže v tomhle případě by to bylo jen info/vyska
a info/sirka
:
while read_nullbyte jmeno barva vyska sirka; do jmeno+=("$jmeno"); barva+=("$barva"); vyska+=("$vyska"); sirka+=("$sirka"); done < <(cat položky.xml \ | relpipe-in-xmltable \ --relation 'položka' \ --records '/polozky/polozka' \ --attribute 'jméno' string 'jmeno' \ --attribute 'barva' string 'barva' \ --attribute 'výška' string 'info/vyska' \ --attribute 'šířka' string 'info/sirka' \ | relpipe-out-nullbyte) for j in ${jmeno[@]}; do echo "jméno: $j"; done for b in ${barva[@]}; do echo "barva: $b"; done for v in ${vyska[@]}; do echo "výška: $v"; done for s in ${sirka[@]}; do echo "šířka: $s"; done
vypíše to:
jméno: prvni jméno: druha jméno: treti barva: cervena barva: zelena barva: modra výška: 100 výška: 200 výška: 300 šířka: 50 šířka: 60 šířka: 70
Taky se někdy může hodit převést stromovou strukturu na plochou.
#!/bin/bash soubor="položky.xml" make "$soubor" xml() { cat "$soubor"; } # xml() { cat ../yamltable/netplan.yaml | yaml2xml; } plocha() { relpipe-in-xmltable \ --relation 'data' \ --records '//text()[normalize-space() != ""]' \ --attribute 'key' string '..' --mode xpath \ --attribute 'value' string '.' } tecky() { relpipe-tr-sed \ --relation '.+' \ --attribute 'key' \ --value '[/\[\]]+' \ --replacement '.' \ --attribute 'key' \ --value '^\.[^\.]+\.' \ --replacement '' } properties() { relpipe-out-ini \ --relation '.*' \ --writer-option dialect java-properties \ --writer-option key-value-separator ' = ' } xml | plocha | relpipe-out-tabular xml | plocha | tecky | properties
Výstup:
data: ╭────────────────────────────────┬────────────────╮ │ key (string) │ value (string) │ ├────────────────────────────────┼────────────────┤ │ /polozky/polozka[1]/jmeno │ prvni │ │ /polozky/polozka[1]/barva │ cervena │ │ /polozky/polozka[1]/info/vyska │ 100 │ │ /polozky/polozka[1]/info/sirka │ 50 │ │ /polozky/polozka[2]/jmeno │ druha │ │ /polozky/polozka[2]/barva │ zelena │ │ /polozky/polozka[2]/info/vyska │ 200 │ │ /polozky/polozka[2]/info/sirka │ 60 │ │ /polozky/polozka[3]/jmeno │ treti │ │ /polozky/polozka[3]/barva │ modra │ │ /polozky/polozka[3]/info/vyska │ 300 │ │ /polozky/polozka[3]/info/sirka │ 70 │ ╰────────────────────────────────┴────────────────╯ Record count: 12
polozka.1.jmeno = prvni polozka.1.barva = cervena polozka.1.info.vyska = 100 polozka.1.info.sirka = 50 polozka.2.jmeno = druha polozka.2.barva = zelena polozka.2.info.vyska = 200 polozka.2.info.sirka = 60 polozka.3.jmeno = treti polozka.3.barva = modra polozka.3.info.vyska = 300 polozka.3.info.sirka = 70
Případně pro ten netplan.yaml:
data: ╭────────────────────────────────────────────────────┬──────────────────╮ │ key (string) │ value (string) │ ├────────────────────────────────────────────────────┼──────────────────┤ │ /yaml/network/version │ 2 │ │ /yaml/network/ethernets/enp84s0/dhcp4 │ false │ │ /yaml/network/ethernets/enp84s0/dhcp6 │ false │ │ /yaml/network/ethernets/enp84s0/accept-ra │ false │ │ /yaml/network/bridges/br0/interfaces[1] │ enp84s0 │ │ /yaml/network/bridges/br0/interfaces[2] │ eth0 │ │ /yaml/network/bridges/br0/addresses │ 192.168.1.101/24 │ │ /yaml/network/bridges/br0/gateway4 │ 192.168.1.1 │ │ /yaml/network/bridges/br0/nameservers/addresses[1] │ 192.168.1.10 │ │ /yaml/network/bridges/br0/nameservers/addresses[2] │ 192.168.1.11 │ │ /yaml/network/bridges/br0/mtu │ 1500 │ │ /yaml/network/bridges/br0/parameters/stp │ true │ │ /yaml/network/bridges/br0/parameters/forward-delay │ 4 │ │ /yaml/network/bridges/br0/dhcp4 │ false │ │ /yaml/network/bridges/br0/dhcp6 │ false │ │ /yaml/network/bridges/br0/accept-ra │ false │ ╰────────────────────────────────────────────────────┴──────────────────╯ Record count: 16
network.version = 2 network.ethernets.enp84s0.dhcp4 = false network.ethernets.enp84s0.dhcp6 = false network.ethernets.enp84s0.accept-ra = false network.bridges.br0.interfaces.1. = enp84s0 network.bridges.br0.interfaces.2. = eth0 network.bridges.br0.addresses = 192.168.1.101/24 network.bridges.br0.gateway4 = 192.168.1.1 network.bridges.br0.nameservers.addresses.1. = 192.168.1.10 network.bridges.br0.nameservers.addresses.2. = 192.168.1.11 network.bridges.br0.mtu = 1500 network.bridges.br0.parameters.stp = true network.bridges.br0.parameters.forward-delay = 4 network.bridges.br0.dhcp4 = false network.bridges.br0.dhcp6 = false network.bridges.br0.accept-ra = false
Na té stránce netplan.yaml je i ukázané, jak se dá vygenerovat víc relací z jednoho XML (--relation
tam může být víckrát, --records
je tam u každé jen jednou).
aktualne to delam pres spocitani polozek a pak rozdelit/parsovat v poctu cyklu pres grep,tac,cat,sed...To by snad mělo jít udělat přes nějaký 'standardní' XML tooling ne?
#!/bin/bash
IFS=$'\n'
jmeno=( $(xmllint --xpath 'polozky/polozka/jmeno/text()' xml.xml) )
barva=( $(xmllint --xpath 'polozky/polozka/barva/text()' xml.xml) )
echo ${jmeno[@]}
echo ${barva[@]}
ISSN 1214-1267, (c) 1999-2007 Stickfish s.r.o.