[gawk] Equivalent voor sed replace?

Pagina: 1
Acties:

Acties:
  • 0 Henk 'm!

  • DieterVDW
  • Registratie: Juli 2002
  • Laatst online: 12-02-2017
Hallo,

Ik ben mijn eerste stapjes in gawk aan het nemen. Ik denk dat ik al behoorlijk door heb wat er allemaal mogelijk is met gawk, maar 1 iets triviaals lukt me nog niet, namelijk een stukje uit een bepaalde lijn halen.

Bv.: met sed:
code:
1
sed -e 's/.*\(iets\).*/\1/g'
(waarbij iets dan meestal ook een regex is)

Dit doet bv. het volgende:
code:
1
2
$ echo een twee drie | sed -e 's/.*\(twee\).*/\1/g'
twee


Met gawk ben ik hier nog niet in geslaagd.
Ik ben op de hoogte van de sub() en gsub() methodes in gawk, maar ik zie niet meteen hoe ik deze effectief kan gebruiken? Het voornaamste probleem is dat ik binnen deze methodes blijkbaar geen backreferences kan gebruiken blijkbaar?

Het volgende werkt bv. niet:
code:
1
2
3
4
5
6
gawk '{ print /iets/ }'
      // Dit print enkel 1.

gawk '{ gsub(^.*\(iets\).*$, "\1") }'
      // Geen idee hoe backreferences mogelijk zijn met gawk?
      // Is het mogelijk om het gematchte gedeelte van een regex in een variabele te stoppen?


Hoe doe ik het equivalent van bovenstaande sed expressie met gawk?


Bij uitbreiding: ik heb een lijst in het volgende formaat:
code:
1
2
3
4
veld1 veld2 "dit is veld 3" veld4 "veld 5 ook met spaties"
veld1 veld2 "dit is veld 3" veld4 "veld 5 ook met spaties"
veld1 veld2 "dit is veld 3" veld4 "veld 5 ook met spaties"
...

(Apache logs in mijn geval.)

Zowel gawk als cut gaan de input op dezelfde manier onderverdelen: door gewoon 1 karakter als delimiter te gaan gebruiken. Er wordt echter geen rekening gehouden met de quotes, dus spaties binnen de quotes worden ook als delimiters aanzien, en veld3 en veld5 worden dan ook onderverdeeld in verschillende velden. Deze input is dan ook niet eenvoudig te parsen...
Is het mogelijk om deze input iets intelligenter te gaan parsen (evt. dmv. gawk) zodat veld3 en veld5 in hun geheel incl. spaties als 1 veld worden aanzien? Hoe kan dit het makkelijkst eenvoudig gebeuren?

Acties:
  • 0 Henk 'm!

Verwijderd

DieterVDW schreef op maandag 05 januari 2009 @ 14:42:
Hallo,

Ik ben mijn eerste stapjes in gawk aan het nemen. Ik denk dat ik al behoorlijk door heb wat er allemaal mogelijk is met gawk, maar 1 iets triviaals lukt me nog niet, namelijk een stukje uit een bepaalde lijn hralen.

Bv.: met sed:
code:
1
sed -e 's/.*\(iets\).*/\1/g'
(waarbij iets dan meestal ook een regex is)

Dit doet bv. het volgende:
code:
1
2
$ echo een twee drie | sed -e 's/.*\(twee\).*/\1/g'
twee


Met gawk ben ik hier nog niet in geslaagd.
Ik ben op de hoogte van de sub() en gsub() methodes in gawk, maar ik zie niet meteen hoe ik deze effectief kan gebruiken? Het voornaamste probleem is dat ik binnen deze methodes blijkbaar geen backreferences kan gebruiken blijkbaar?

Het volgende werkt bv. niet:
code:
1
2
3
4
5
6
gawk '{ print /iets/ }'
      // Dit print enkel 1.

gawk '{ gsub(^.*\(iets\).*$, "\1") }'
      // Geen idee hoe backreferences mogelijk zijn met gawk?
      // Is het mogelijk om het gematchte gedeelte van een regex in een variabele te stoppen?


Hoe doe ik het equivalent van bovenstaande sed expressie met gawk?
code:
1
2
echo 'een twee drie' | gawk '{ match($0, /.*(twee).*/, matches); print matches[1]; }'
twee


Wel GNU-awk specifiek. Zal niet werken in mawk en 'the one true awk' van Kernighan, zoals vaak gebruikt in de BSDs. Zie ook de uitstekende GNU AWK info pages.
Bij uitbreiding: ik heb een lijst in het volgende formaat:
code:
1
2
3
4
veld1 veld2 "dit is veld 3" veld4 "veld 5 ook met spaties"
veld1 veld2 "dit is veld 3" veld4 "veld 5 ook met spaties"
veld1 veld2 "dit is veld 3" veld4 "veld 5 ook met spaties"
...

(Apache logs in mijn geval.)

Zowel gawk als cut gaan de input op dezelfde manier onderverdelen: door gewoon 1 karakter als delimiter te gaan gebruiken. Er wordt echter geen rekening gehouden met de quotes, dus spaties binnen de quotes worden ook als delimiters aanzien, en veld3 en veld5 worden dan ook onderverdeeld in verschillende velden. Deze input is dan ook niet eenvoudig te parsen...
Is het mogelijk om deze input iets intelligenter te gaan parsen (evt. dmv. gawk) zodat veld3 en veld5 in hun geheel incl. spaties als 1 veld worden aanzien? Hoe kan dit het makkelijkst eenvoudig gebeuren?
Dit is niet in een one-line op te lossen in AWK. Perl en Python hebben hier wel modules voor, maar om dit met AWK op te lossen zul je waarschijnlijk moeten itereren over de velden, kijken of een veld begint met een ", en zolang velden blijven appenden aan een tijdelijke variabele tot je aan de closing " komt.

Acties:
  • 0 Henk 'm!

  • benoni
  • Registratie: November 2003
  • Niet online
code:
1
awk 'BEGIN { n = 0; } { delete a; s = false; i = 1; split($0, a1, "\\\\\""); for( i1 = 1; i1 <= length(a1); i1++ ) { if( i1 > 1 ) a[i] = a[i] "\""; split(a1[i1], a2, "\""); for( i2 = 1; i2 <= length(a2); i2++ ) { if( i2 > 1 ) s = !s; if(s) { a[i] = a[i] a2[i2] } else { split(a2[i2], a3, "[\t ]+"); for( i3 = 1; i3 <= length(a3); i3++ ) { if( i3 > 1 ) i++; a[i] = a[i] a3[i3]; } } } } n++; print "<rec n=\"" n "\">"; for( i4 = 1; i4 <= i; i4++ ) { print "\t<f" i4 ">" a[i4] "</f" i4 ">" }; print "</rec>"; }' log.txt


:P

Acties:
  • 0 Henk 'm!

  • DieterVDW
  • Registratie: Juli 2002
  • Laatst online: 12-02-2017
Hmm gawk is precies toch ook niet het wondermiddel waarvan ik het verdacht te zijn ... :)

Acties:
  • 0 Henk 'm!

  • benoni
  • Registratie: November 2003
  • Niet online
For what it's worth, de oneliner toch maar een beetje vormgegeven en becommentarieerd O-)

Bash:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
awk ' \
    BEGIN { \
        n = 0; \
    } \
    { \
        # \
        # Refresh output array \
        delete a; \
        i = 1; \
        # \
        # Switch: inside/outside quotes \
        s = false; \
        # \
        # Split to filter out escaped quotes \
        split ($0, a1, "\\\\\""); \
        for (i1 = 1; i1 <= length(a1); i1++) { \
            # \
            # Write those as normal quotes to output \
            if (i1 > 1) a[i] = a[i] "\""; \
            # \
            # Split for value delimiting quotes \
            split (a1[i1], a2, "\""); \
            for (i2 = 1; i2 <= length(a2); i2++) { \
                # \
                # Flip the quotes switch \
                if (i2 > 1) s = !s; \
                # \
                # If we read within quotes... \
                if (s) { \
                    # \
                    # ...read data into current field... \
                    a[i] = a[i] a2[i2]; \
                } \
                else { \
                    # \
                    # ...otherwise split by tabs and spaces \
                    split(a2[i2], a3, "[\t ]+"); \
                    for (i3 = 1; i3 <= length(a3); i3++) { \
                        # \
                        # Select next field \
                        if (i3 > 1) i++; \
                        # \
                        # Read data into field \
                        a[i] = a[i] a3[i3]; \
                    } \
                } \
            } \
        } \
        # \
        # Count records: \
        n++; \
        # \
        # Some XML-ish output: \
        print "<rec n=\"" n "\">"; \
        for (i4 = 1; i4 <= i; i4++) { \
            print "\t<f" i4 ">" a[i4] "</f" i4 ">" \
        }; \
        print "</rec>"; \
    }' log.txt


Misschien toch handig voor later :>