[Megoldva] Bash scriptelés, string eltávolítás

Sziasztok!

Van egy olyan problémám, hogy egy log fájlból kellene bash scriptel kiszedni bizonyos részeket.
A részek < és > karakter között vannak. A < és > karakterekkel együtt kellene eltávolítanom.
Arra gondoltam, hogy egy tömbszerűségben meg lehetne adni azokat a részeket amiket ki akarok szedni, és a végeredményt egy másik fájlba menteném.

Ha valaki tudna segíteni, megköszönném.

Hozzászólások

sed 's/<[^<]*>//g'

Már, ha jól értem. Ez valami házi feladat?

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE


#!/bin/bash

myname="${0##*/}"
if [ $# -ne 1 ]; then
    echo "$myname <infile>" >&2
    exit 1
fi
if ! [ -f "$1" -a -r "$1" ]; then
    echo "A(z) '$1' bemeneti file nem olvasható" >&2
    exit 1
fi
sed 's/<[^<]*>//g' "$1"
exit 0

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

sed 's/<[^<]*>//g' log.file >> logs.file
nem kell cat, nem kell külön változó.
Egyébként nem teljesen értem a dolgot. Ha van egy ilyen sor:
bla1 < cucc1 > bla2 < cucc2 > bla3
akkor miért nem lesz az eredmény „bla1 bla3” ?

int getRandomNumber() { return 4; }  // ← aláírás
//szabályos kockadobással választva. garantáltan véletlenszerű.  xkcd

Így jobban megnézve a parserem hibázott, my bad. Általában a 's/<[^>]*>//g' mintát szoktam ellenőrizni, de alapvetően valóban nincs különbség.

int getRandomNumber() { return 4; }  // ← aláírás
//szabályos kockadobással választva. garantáltan véletlenszerű.  xkcd

Valóban az a jó, amit Te írtál. Valójában azt nem értem, az enyém mitől működött. :)

Hiszen, ha jól gondolom, a [^<] illeszkedik a > jelre, így azon túl kellene mennie, nem?

Lényegében azt akartam írni, amit Te, s nem értem, miért nem azt írtam, azt meg végképp nem értem, mégis mitől működik. Nem szeretem, ha valami úgy működik, hogy nem értem, miért teszi.

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Alapvetően annyit akarunk ellenőrizni, hogy a < és a > egy „HTML tag” részét képezze. Amennyiben a stringünk nem tartalmaz <…<…>-hez hasonló anomáliákat (amiket azt hiszem, az SGML egyébként enged), kb. mindegy, hogy a tagnyitó <-re, vagy a taget záró >-ra keresünk a vélt tagen belül. Szerintem legalábbis.
A tagekre matcheltetést mostanában már általában nem szoktam végiggondolni, talán sajnos.

int getRandomNumber() { return 4; }  // ← aláírás
//szabályos kockadobással választva. garantáltan véletlenszerű.  xkcd

Pedig benned volt minden reményem, hogy megtudjam. Mert szerintem én írtam marhaságot, s nem értem, mitől működik. Persze, azt tegyük fel, hogy tag-en belül nincs kacsacsőr. Ugya nekem az a bajom, hogy amikor eléri a záró kacsacsőrt, akkor a [^<] erre éppen úgy illeszkedik, mint a tag bármely belső karakterére, tehát tovább kellene mennie a match-nek a következő nyitó tag-ig.

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Véletlenül pont működik így is. :) De azért a hatékonyság miatt jobb a

<[^>]*>

, mint a

<[^<]*>

, mert utóbbi esetben a


<tag>hosszuszoveg</tag>

illesztésekor a regexp engine előszőr elmegy záró tag elejéig a * miatt, onnan pedig length(hosszuszoveg) lépésszámú backtracking-et kell végrehajtania, hogy megtalálja amit keres. És ezt minden egyes tagnél.

Persze biztosan valahogy optimalizálják az okosak az ilyen sed meg hasonló cuccokat, szóval lehet hogy mégsem számít. :)

Most, hogy írtad, rájöttem, hogy mitől működik az én változatom. Hiába megy első körben el a záró tag elejéig, a > jelre illeszkedés nem lesz meg, csak akkor, ha valóban visszamegy az első tag végéig. Ez nem esett le elsőre.

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

"
<[^<]*>
...
<tag>hosszuszoveg</tag>

illesztésekor a regexp engine előszőr elmegy záró tag elejéig a * miatt,"

Nincs miért elmennie. Szó szerint leíratik neki, hogy kisebbjelet követő akárhány nemkisebbjel után legyen egy nagyobbjel, ez pedig (ahogy fentebb utalt vki erre), pont ugyanúgy az adott nyitó/záró tagen belül azonnal illeszkedik, mint a másik felfogás, ha nincs matrjoskaszerű tag. Ilyenkor a két megoldás teljesen ekvivalens.

Szerintem is el kell menjen a következő tag nyitó kacsacsőréig, hiszen az illeszkedés a legbővebb halmazra történik:

sed 's/<.*>//g' <<<'<a tag>egy</a> ketto<b tag> harom</b> negy'
 negy

Ha úgy lenne, ahogy írod, akkor ez is azt csinálná, ami az eredeti feladat volt. Az más kérdés, hogy amikor elmegy a nyitó kacsacsőrig, ott döbben rá, hogy nem teljesül az illeszkedés, s közben volt egy olyan lehetősége, ahol meg igen.

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Ne keverd a felhozott (rossz) <.*> példát a fent tárgyalttal!

A .* és [^>]* között tényleg nincs nagy különbség, de ha fejben leprogramozod, hogy egyszerű substr-ekkel meddig tartana megtalálni egy minta elejét az egyikkel és a másikkal, kiderül, hogy mégis hatalmas az a diffi.

Sajnos nem ekvivalensek. Értem a gondolatmenetedet, de mint ahogy mások is írták, a '*' greedy. Ezért hiába találja meg az egyezést, az elején, definíció szerint tovább kell mennie (kötelezően) mindaddig, míg a megelőző részkifejezés illeszkedik. Tehát ha a példában ezt írtam volna:


<tag>nemjolformalt>xml</tag>

Akkor az illeszkedés


<tag>nemjolformalt>

lenne, és még ráadásul backtracking-gel megfűszerezve, mert így is elmenne a záró tag elejéig.

Kimenete az stdout, tehát ha a script neve tagremove, az aktuális könyvtárban van, van rá futtatási jog, akkor így használható:

./tagremove /itt/van/a/logfile/log.txt >eredmeny.txt

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE