levinus
Anmeldungsdatum: 15. Oktober 2015
Beiträge: 24
|
Hey,
ich arbeite an einer Datenbank, die sich selbst aus einem Kalender heraus updated. Somit extrahiere ich aus einer .ics Datei zB Terminstart.
Das hab ich so realisiert:
| cat basic.ics | awk '/DTSTART/ { #DTSTART für Terminstart finden
{print substr($0,9,4) "-" substr($0,13,2) "-" substr($0,15,2)} #Etwas umformatieren und ausspucken
}' | sed -n "$id p" #Nur eine bestimmte Zeile auswählen
|
Nun will ich die Terminbeschreibung exportieren. Das Problem: Diese geht meist über eine Zeile hinaus, so funktioniert mein obiges Skript nicht.
Da ich nicht sehr awk-bewandert bin habe ich mich mal umgesehen, und ein Skript, dass ähnliches realisiert gefunden und geändert:
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 | BEGIN { status = 0; } #Init
{
if (match($0, "DESCRIPTION:" descr)) #Finde Pattern
{
status = 1; #Ab jetzt: Bearbeitungsblock
idx = 1; #Index für das Array
txt = substr($0, 13, 1000); #DESCRIPTION aus der ersten Zeile kürzen
data[idx] = txt; #Erste Zeile speichern
next; #Nächste Zeile
}
}
/LAST-MODIFIED:/ { #Pattern finden
if ( status = 1 ) #Mach nur weiter, wenn wir in einem Bearbeitungsblock sind.
{
for ( i = 0; i < idx; i++ )
{ printf("%s",data[i]); } #Gib alle gesammelten Datensätze aus
delete data; #Lösche temp-Daten
status = 0; #Bearbeitungsblock vorbei
}
next;
}
{
if ( status != 1 ) #Wenn gerade kein Bearbeitungsblock: Weiterspringen
{
next;
}
data[++idx] = $0; #Aktuelle Zeile zu Array hinzufügen
}
END { print "\n" }
|
Hier wird ein Bearbeitungsblock geöffnet, wenn "DESCRIPTION:" + Zusatz gefunden wird. Alle danach folgenden Zeilen, bis zum LAST-MODIFIED: (nächste Einstl. in der .ics) werden in einem Array gespeichert und wieder ausgegeben. Der Zusatz wird beim Aufruf von awk mit -v übergeben und soll den ersten Teil des nachfolgenden Strings, den ich haben will, enthalten. Diesen habe ich vorher mit der ersten Methode ermittelt. D.h. awk sucht nach "DESCRIPTION:Erstere Zeile der Beschreibung..." und gibt dann im besten Fall die komplette Beschreibung zurück. Ich bekomme aber immer nur die erste Zeile. Außerdem habe ich das Problem, dass in der Beschreibung auch Zeilenbrüche (\n) sind. Diese kann match() aus irgendeinem Grund aber nicht detecten. Mit // wäre das möglich, wenn man vor den Backslash einen weiteren Backslash setzt. Jedoch kennt // keine Variablen. Gibt es dafür irgendeine Lösung? Bin ich komplett blind? (Oder einfach nur ein Noob?) Es wäre sehr dankbar, wenn mir jemand erklären könnte welche Fehler ich gemacht habe und inwiefern in das Skript verändern müsste. Oder ob es dafür vielleicht eine Super-Duper-Lösung gibt, die ich einfach nicht gesehen habe. ☺ MfG,
Levin
|
rklm
Projektleitung
Anmeldungsdatum: 16. Oktober 2011
Beiträge: 12822
|
levinus schrieb:
Ich bekomme aber immer nur die erste Zeile. Außerdem habe ich das Problem, dass in der Beschreibung auch Zeilenbrüche (\n) sind. Diese kann match() aus irgendeinem Grund aber nicht detecten.
Hmm... 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 | $ awk --version
GNU Awk 4.1.3, API: 1.1 (GNU MPFR 3.1.4, GNU MP 6.1.0)
Copyright (C) 1989, 1991-2015 Free Software Foundation.
...
$ awk 'BEGIN {x="a\nb";print x;a[0]="";if(match(x, /a+\n*b+/, a)){print ">" a[0] "<"}}'
a
b
>a
b<
$ awk 'BEGIN {x="a\nb";print x;a[0]="";if(match(x, /a+[[:space:]]*b+/, a)){print ">" a[0] "<"}}'
a
b
>a
b<
$ mawk -W version
mawk 1.3.3 Nov 1996, Copyright (C) Michael D. Brennan
compiled limits:
max NF 32767
sprintf buffer 1020
$ mawk 'BEGIN {x="a\nb";print x;if(match(x, /a+\n*b+/)){print ">" substr(x,RSTART,RLENGTH) "<"}}'
a
b
>a
b<
$ mawk 'BEGIN {x="a\nb";print x;if(match(x, /a+[ \n]*b+/)){print ">" substr(x,RSTART,RLENGTH) "<"}}'
a
b
>a
b<
|
Ciao robert
|
levinus
(Themenstarter)
Anmeldungsdatum: 15. Oktober 2015
Beiträge: 24
|
rklm schrieb: levinus schrieb:
Ich bekomme aber immer nur die erste Zeile. Außerdem habe ich das Problem, dass in der Beschreibung auch Zeilenbrüche (\n) sind. Diese kann match() aus irgendeinem Grund aber nicht detecten.
Hmm... 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 | $ awk --version
GNU Awk 4.1.3, API: 1.1 (GNU MPFR 3.1.4, GNU MP 6.1.0)
Copyright (C) 1989, 1991-2015 Free Software Foundation.
...
$ awk 'BEGIN {x="a\nb";print x;a[0]="";if(match(x, /a+\n*b+/, a)){print ">" a[0] "<"}}'
a
b
>a
b<
$ awk 'BEGIN {x="a\nb";print x;a[0]="";if(match(x, /a+[[:space:]]*b+/, a)){print ">" a[0] "<"}}'
a
b
>a
b<
$ mawk -W version
mawk 1.3.3 Nov 1996, Copyright (C) Michael D. Brennan
compiled limits:
max NF 32767
sprintf buffer 1020
$ mawk 'BEGIN {x="a\nb";print x;if(match(x, /a+\n*b+/)){print ">" substr(x,RSTART,RLENGTH) "<"}}'
a
b
>a
b<
$ mawk 'BEGIN {x="a\nb";print x;if(match(x, /a+[ \n]*b+/)){print ">" substr(x,RSTART,RLENGTH) "<"}}'
a
b
>a
b<
|
Ciao robert
So wie ich das verstehe willst du mich darauf hinweisen, dass man auch mit match() \n suchen kann. Ich gebe zu, dass meine Ausdrucksweise da nicht ganz korrekt war (es geht schon, aber nicht ohne // zu benutzen), jedoch habe ich danach erwähnt, dass ich mit den "Regular Expressions Slashes" nicht arbeiten kann, da diese keine Variablen unterstützen und alles 'wörtlich' nehmen. Oder hab ich da was übersehen?
|
rklm
Projektleitung
Anmeldungsdatum: 16. Oktober 2011
Beiträge: 12822
|
levinus schrieb:
So wie ich das verstehe willst du mich darauf hinweisen, dass man auch mit match() \n suchen kann. Ich gebe zu, dass meine Ausdrucksweise da nicht ganz korrekt war (es geht schon, aber nicht ohne // zu benutzen), jedoch habe ich danach erwähnt, dass ich mit den "Regular Expressions Slashes" nicht arbeiten kann, da diese keine Variablen unterstützen und alles 'wörtlich' nehmen. Oder hab ich da was übersehen?
Eher hast Du zu viel gesehen. Wo siehst Du in meinem Beispiel // ? Achso, Du meinst beim Argument von match() . Na dann: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 | $ mawk 'BEGIN {x="a\nb";print x;if(match(x, /a+\n*b+/)){print ">" substr(x,RSTART,RLENGTH) "<"}}'
a
b
>a
b<
$ mawk 'BEGIN {x="a\nb";print x;if(match(x, "a+\n*b+")){print ">" substr(x,RSTART,RLENGTH) "<"}}'
a
b
>a
b<
$ mawk 'BEGIN {x="a\nb";print x;r="a+\n*b+";if (match(x,r)){print ">" substr(x,RSTART,RLENGTH) "<"}}'
a
b
>a
b<
|
|
levinus
(Themenstarter)
Anmeldungsdatum: 15. Oktober 2015
Beiträge: 24
|
rklm schrieb: levinus schrieb:
So wie ich das verstehe willst du mich darauf hinweisen, dass man auch mit match() \n suchen kann. Ich gebe zu, dass meine Ausdrucksweise da nicht ganz korrekt war (es geht schon, aber nicht ohne // zu benutzen), jedoch habe ich danach erwähnt, dass ich mit den "Regular Expressions Slashes" nicht arbeiten kann, da diese keine Variablen unterstützen und alles 'wörtlich' nehmen. Oder hab ich da was übersehen?
Eher hast Du zu viel gesehen. Wo siehst Du in meinem Beispiel // ? Achso, Du meinst beim Argument von match() . Na dann:
Diese 'Skripte' funktionieren bei mir auch einwandfrei. Aber, wie man im ersten Beitrag schon sieht benutze ich ja die match() Syntax aus deinem 2. Command. Im Skript geht es dann nämlich nicht:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 | $ echo "DESCRIPTION:a\nb" > test.ics
$ echo "LAST-MODIFIED:" >> test.ics
$ cat test.ics
DESCRIPTION:a\nb
LAST-MODIFIED:
$ awk -v descr="" -f new.awk test.ics
a\nb
$ awk -v descr="a" -f new.awk test.ics
a\nb
$ awk -v descr="a\n" -f new.awk test.ics
$ awk -v descr="a\nb" -f new.awk test.ics
|
Getestet mit der obenstehenden Version des Skripts, so wie mit einem geänderten match() Part:
| final = "DESCRIPTION:" descr
if (match($0, final))
|
Was man natürlich machen kann ist mit Ausdrücken, wie (.) zu arbeiten. Dann funktioniert das ganze soweit.
Was allerdings nichts daran ändert, dass mein Skript im allgemeinen immernoch nicht die richtigen Zeilen ausspuckt -.-
|
levinus
(Themenstarter)
Anmeldungsdatum: 15. Oktober 2015
Beiträge: 24
|
Problem gelöst, es gab ein Problem mit der for-Schleife. Ich habe sie testweise durch eine while-Schleife ersetz und alles funktioniert.
| i = 0;
while ( i <= idx )
{
printf("%s",data[i]);
i = i + 1;
}
|
Danke für deine Hilfe, rklm.
|
rklm
Projektleitung
Anmeldungsdatum: 16. Oktober 2011
Beiträge: 12822
|
levinus schrieb: rklm schrieb: levinus schrieb:
So wie ich das verstehe willst du mich darauf hinweisen, dass man auch mit match() \n suchen kann. Ich gebe zu, dass meine Ausdrucksweise da nicht ganz korrekt war (es geht schon, aber nicht ohne // zu benutzen), jedoch habe ich danach erwähnt, dass ich mit den "Regular Expressions Slashes" nicht arbeiten kann, da diese keine Variablen unterstützen und alles 'wörtlich' nehmen. Oder hab ich da was übersehen?
Eher hast Du zu viel gesehen. Wo siehst Du in meinem Beispiel // ? Achso, Du meinst beim Argument von match() . Na dann:
Diese 'Skripte' funktionieren bei mir auch einwandfrei.
Na also! Du brauchst das auch gar nicht in Anführungsstriche zu setzten, denn es sind tatsächlich Skripte.
Aber, wie man im ersten Beitrag schon sieht benutze ich ja die match() Syntax aus deinem 2. Command. Im Skript geht es dann nämlich nicht:
| $ echo "DESCRIPTION:a\nb" > test.ics
$ echo "LAST-MODIFIED:" >> test.ics
$ cat test.ics
DESCRIPTION:a\nb
LAST-MODIFIED:
|
Scherzkeks: Du hast da keinen Zeilenumbruch in der Datei, sondern einen Backslash gefolgt von einem "n". 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 | $ echo "DESCRIPTION:a\nb"
DESCRIPTION:a\nb
$ echo -e "DESCRIPTION:a\nb"
DESCRIPTION:a
b
$ echo "DESCRIPTION:a\nb" | od -t x1c
0000000 44 45 53 43 52 49 50 54 49 4f 4e 3a 61 5c 6e 62
D E S C R I P T I O N : a \ n b
0000020 0a
\n
0000021
$ echo -e "DESCRIPTION:a\nb" | od -t x1c
0000000 44 45 53 43 52 49 50 54 49 4f 4e 3a 61 0a 62 0a
D E S C R I P T I O N : a \n b \n
0000020
|
Getestet mit der obenstehenden Version des Skripts, so wie mit einem geänderten match() Part:
| final = "DESCRIPTION:" descr
if (match($0, final))
|
Poste lieber noch einmal das komplette Skript.
Was man natürlich machen kann ist mit Ausdrücken, wie (.) zu arbeiten. Dann funktioniert das ganze soweit.
Wenn Du mit einem regulären Ausdruck Mustererkennung betreiben willst, musst Du das Muster natürlich so definieren, dass es die Dinge erfasst, die Du erfassen willst. Klingt seltsam tautologisch, ist aber trotzdem richtig.
Was allerdings nichts daran ändert, dass mein Skript im allgemeinen immernoch nicht die richtigen Zeilen ausspuckt -.-
s.o. levinus schrieb: Problem gelöst, es gab ein Problem mit der for-Schleife. Ich habe sie testweise durch eine while-Schleife ersetz und alles funktioniert.
Ich glaube nicht, dass es ein Problem mit der for -Schleife gab.
| i = 0;
while ( i <= idx )
{
printf("%s",data[i]);
i = i + 1;
}
|
Die Schleife ist nicht äquivalent zu der anderen, weil die Bedingung anders ist.
Danke für deine Hilfe, rklm.
Bitte. Ich glaube allerdings, dass Du noch nicht am Ziel bist (s.o.). Wenn es jetzt läuft, dann hast Du das m.E. eher zufällig repariert. Es lag jedenfalls eher nicht an der Schleife - es sei denn, die Bedingung war falsch. Aber i <= idx kannst Du natürlich auch in der for -Schleife verwenden.
|
levinus
(Themenstarter)
Anmeldungsdatum: 15. Oktober 2015
Beiträge: 24
|
rklm schrieb: Die Schleife ist nicht äquivalent zu der anderen, weil die Bedingung anders ist.
Ja, ich weiß, da habe ich mich vermutlich wieder falsch ausgedrückt. Ich habe auch vorher mit der for-Schleife rumprobiert und die gleiche Bedingung benutzt. Dann hab ichs umgebaut und mit der while-Schleife probiert. Dann gings.
rklm schrieb: Scherzkeks: Du hast da keinen Zeilenumbruch in der Datei, sondern einen Backslash gefolgt von einem "n".
Klar sind das Zeilenumbrüche. Vielleicht nicht für die Bash oder AWK, für andere Programme aber schon. Ganzes Skript:
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 |
BEGIN { status = 0; }
{
final = "DESCRIPTION:" descr
if (match($0, final))
{
status = 1;
idx = 1;
txt = substr($0, 13, 1000);
data[idx] = txt;
next;
}
}
/LAST-MODIFIED:/ {
if ( status == 1 )
{
i = 0;
while ( i <= idx )
{
sep = "\n";
printf("%s",data[i]);
i = i + 1;
}
delete data;
status = 0;
}
next;
}
{
if ( status != 1 )
{
next;
}
idx = idx + 1;
data[idx] = $0;
}
END {}
|
|
rklm
Projektleitung
Anmeldungsdatum: 16. Oktober 2011
Beiträge: 12822
|
levinus schrieb:
rklm schrieb: Scherzkeks: Du hast da keinen Zeilenumbruch in der Datei, sondern einen Backslash gefolgt von einem "n".
Klar sind das Zeilenumbrüche. Vielleicht nicht für die Bash oder AWK, für andere Programme aber schon.
Achso, die sind escaped! Tut mir leid, das hatte ich nicht kapiert. Meine Lösung sieht so aus: | BEGIN { RS="\r\n" }
/^DESCRIPTION:/ { desc = substr($0, 13) }
desc != "" && /^ / { desc = desc substr($0, 1) }
desc != "" && /^[^ ]/ && $0 !~ /^DESCRIPTION:/ { gsub(/\\n/,"\n", desc); print desc; desc = "" }
|
Etwas netter formatiert: | BEGIN { RS = "\r\n" }
/^DESCRIPTION:/ { desc = substr($0, 13) }
desc != "" && /^ / { desc = desc substr($0, 1) }
desc != "" && /^[^ ]/ && $0 !~ /^DESCRIPTION:/ {
gsub(/\\n/,"\n", desc)
print desc
desc = ""
}
|
|