Olisi tämmöinen kinkkisempi ongelma liittyen XML-dokumentin parsintaan regular expression-lauseilla Linux-palvelimella. Tiedän, että Linuxilla olisi esim. xmllint ja siinä xpath, mutta tällä ko. palvelimella ei ole tuota xpathia. Lisäksi järjestelmä, joka lukee XML-dokkareita, käyttää vain regexp-lauseita, joten niillä on nyt pärjättävä.
Ongelma on siis seuraava. Alla on malli XML-dokumentista, josta pitäisi saada luettua esim. tuon ACCESSORIES Entryn Name-kentän sisältö regexpejä käyttäen.
<?xml version="1.0" encoding="UTF-8"?> <Records> <Record> <Entry> <Type>TOOLS</Type> <Code>3015</Code> <DepotCode>T-2345-1</DepotCode> <Name>Product name`3015</Name> </Entry> <Entry> <Type>ACCESSORIES</Type> <Code>1002</Code> <DepotCode>A-3345-1</DepotCode> <Name>Product name 1002</Name> </Entry> <Entry> <Type>MACHINES</Type> <Code>3321</Code> <DepotCode>M-2123-1</DepotCode> <Name>Product name 3321</Name> </Entry> </Record> </Records>
Olen testannut XML-dokumentin parsintaa komentorivillä seuraavasti:
cat record.xml | tr -d '\n\r' |grep -i -E -o '(<Type>ACCESSORIES</Type>).*(</Name></Entry>)' |sed -n -e 's/.*<Name>\(.*\)<\/Name>.*/\1/p'
Eli aluksi rivitän koko dokumentin tuolla tr -d komennolla. Sen jälkeen olisi tarkoitus grepata tuo ACCESSORIES Entry ja lukea se lopputagiin saakka. Sitten sediä käyttäen haluaisin "riisua" tuon Name-kentän, jotta jäljelle jää vain sen sisältö.
Tuo nyt ei kuitenkaan ihan toimi, sillä tuloksena on vain viimeisimmän Entryn, eli MACHINES-entryn Name-kentän sisältö. Miten saisin poimittua/tulostettua pelkästään tuon haluamani entryn Name-kentän sisällön?
Jos käytössä on GNU grep, niin koko homma onnistuisi seuraavasti:
grep -izoP '(?s)<Type>ACCESSORIES</Type>.*?<Name>\K[^<]*' record.xml
Tulostaa:
Product name 1002
Eli:
-i ignore-case (omasta esimerkistäsi, eli ilmeisesti koet tarvitsevasi tätä)
-z käsitellään rivivaihdot nullina (tämä ja (?s) yhdessä mahdollistavat ettei tr -komentoa tarvita)
-o tulostetaan vain osuma
-P käytetään Perl tyylisiä regexejä
(?s) aktiovoi PCRE_DOTALL:in, jolloin . vastaa mitä vaan merkkiä ja rivivaihtoa
<Type>ACCESSORIES</Type> Pitää täsmätä
.*? ottaa mahdollisimman vähän merkkejä
<Name> Pitää täsmätä
\K[^<]* "Osuma" on tästä eteenpäin [^<]* eli kaikki < -merkkiin asti.
Kiitos vastauksesta! Valitettavasti tuo ei näytä toimivan ko. Linux-palvelimella, sillä saan oheisen virheilmoituksen. Eli ilmeisesti GNU grep:ä ei ole käytössä palvelimella.
grep: The -P and -z options cannot be combined
Kokeilin ajaa komentoa myös ilman noita -P ja -z optioita, eli lisäsin tr -d komennon ennen grepiä, mutta tämä ei tulostanut mitään.
Joo, Linuxissa (Debian) itsekin ajoin. Toi -P ja -z optioiden yhtäaikaisuuden mahdottomuus kielii ehkä siitä että -P tarkoittaa tuossa sinun versiossasi jotain muuta.
Itsellä grepin man-sivusta relevantti kohta:
man grep kirjoitti:
-P, --perl-regexp
Interpret PATTERN as a Perl regular expression (PCRE, see below). This is highly experimental and grep -P may warn of unimplemented features.
Yleisesti ottaen menee vähän tylsemmäksi tuon tekeminen tuolla versiolla grepistä, jossa ei ole PCRE tukea. Edes lazy modifierit ei toimi :( Muutenhan olis helppo tuupata tuohon omaan riviisi <Type>ACCESSORIES</Type>.*?<Name>.*?</Name>
Lisäys:
Tässä nyt vielä toimiva versio noilla käyttämilläsi työkaluilla.
Koska lazy modifieria ei ollut käytössä, niin annoin tuon ennen <Name> tulevan osuuden sisältää vain:
- [^<] muita merkkejä kuin tagin aloittavia
- <[^/] tagin alkuja, jotka ei ole lopputageja tai
- </[^E] lopputageja, jotka alkaa muulla kuin E.
Eli näin löytyvä (ahne) pätkä ei voi sisältää </Entry> :ä.
cat record.xml | tr -d '\n\r' |grep -iEo '<Type>ACCESSORIES</Type>([^<]|<[^/]|</[^E])*<Name>[^<]*</Name>' | sed -ne 's/.*<Name>\(.*\)<\/Name>.*/\1/p'
Kokeilin tuota komentoa/riviä ja se toimii. Kiitokset avusta!
Grez kirjoitti:
cat record.xml | tr -d '\n\r' |grep -iEo '<Type>ACCESSORIES</Type>([^<]|<[^/]|</[^E])*<Name>[^<]*</Name>' | sed -ne 's/.*<Name>\(.*\)<\/Name>.*/\1/p'
Nyt se tosiaan hakee vain tuon ACCESSORIES-entryn ja palauttaa sen <Name>-elementin sisällön. Kyllähän näitä varten olisi kätevämpiäkin työkaluja, mutta, vanha systeemi ja vanhat menetelmät.. :/
Aihe on jo aika vanha, joten et voi enää vastata siihen.