AWK sub

Vom einfachen Programm zum fertigen Debian-Paket, Fragen rund um Programmiersprachen, Scripting und Lizenzierung.
Antworten
inne
Beiträge: 3273
Registriert: 29.06.2013 17:32:10
Lizenz eigener Beiträge: GNU General Public License
Kontaktdaten:

AWK sub

Beitrag von inne » 08.07.2017 10:30:48

Hallo,

Code: Alles auswählen

$ echo http://example.org: foo bar baz | sed 's#\(.*\):.*#\1#'
http://example.org
Ich will also nur den URL haben und die Zusatzinformation hinter dem Doppelpunkt weg.

Bekomme ich das auch mit der Funktion sub in AWK hin, bzw. wie wird sowas in AWK gelöst?
Ganz dumm habe ich es mal so probiert:

Code: Alles auswählen

$ echo http://example.org: foo bar baz | awk '{sub("\(.*\):.*", "\1"); print}'
PS: Wie nennt man nochmal das \1 im RegEx?

Benutzeravatar
Meillo
Moderator
Beiträge: 8782
Registriert: 21.06.2005 14:55:06
Wohnort: Balmora
Kontaktdaten:

Re: AWK sub

Beitrag von Meillo » 08.07.2017 10:38:26

In awk macht man das so (das kann IIRC naemlich kein \1 & Co.):

Code: Alles auswählen

sub(/:.*/, "")
Und in sed ist der Ansatz auch effizienter und auch kuerzer zu tippen: s/:.*//
Use ed once in a while!

inne
Beiträge: 3273
Registriert: 29.06.2013 17:32:10
Lizenz eigener Beiträge: GNU General Public License
Kontaktdaten:

Re: AWK sub

Beitrag von inne » 08.07.2017 10:45:47

Deins gibt mir auch nur "http" zurück.

Ich habe nun das hier als Lösung für mich, aber mir will kein besserer RegEx einfallen:

Code: Alles auswählen

$ echo http://example.org: foo bar baz | awk '{ str=substr($0, 0, match($0, ": ") - 1); print str}'
http://example.org
Zumindest kann ich damit nun den system()-Aufruf füttern...
Zuletzt geändert von Anonymous am 08.07.2017 11:31:28, insgesamt 1-mal geändert.

inne
Beiträge: 3273
Registriert: 29.06.2013 17:32:10
Lizenz eigener Beiträge: GNU General Public License
Kontaktdaten:

Re: AWK sub

Beitrag von inne » 08.07.2017 11:03:04

inne hat geschrieben: ↑ zum Beitrag ↑
08.07.2017 10:30:48
PS: Wie nennt man nochmal das \1 im RegEx?
Nennt man im deutschen wohl Gruppe.

Benutzeravatar
Meillo
Moderator
Beiträge: 8782
Registriert: 21.06.2005 14:55:06
Wohnort: Balmora
Kontaktdaten:

Re: AWK sub

Beitrag von Meillo » 08.07.2017 13:43:57

inne hat geschrieben: ↑ zum Beitrag ↑
08.07.2017 10:45:47
Deins gibt mir auch nur "http" zurück.
Ja, stimmt. So ist das wenn man Code nur aus dem Kopf hinschreibt und nicht testet: Hatte den ersten Doppelpunkt uebersehen.

Hier zwei neue Vorschlaege:

Code: Alles auswählen

sub(/: .*/, ""); print;
(Also mit einem Space hinter dem Doppelpunkt.)

Oder:

Code: Alles auswählen

sub(/:$/, "", $1); print $1;
Use ed once in a while!

Benutzeravatar
Meillo
Moderator
Beiträge: 8782
Registriert: 21.06.2005 14:55:06
Wohnort: Balmora
Kontaktdaten:

Re: AWK sub

Beitrag von Meillo » 08.07.2017 13:52:44

inne hat geschrieben: ↑ zum Beitrag ↑
08.07.2017 11:03:04
inne hat geschrieben: ↑ zum Beitrag ↑
08.07.2017 10:30:48
PS: Wie nennt man nochmal das \1 im RegEx?
Nennt man im deutschen wohl Gruppe.
Hier ist die passende Beschreibung dazu aus POSIX (auf Englisch): http://pubs.opengroup.org/onlinepubs/96 ... g_09_03_06

Das in Klammern ist also eine sub-expression ((gruppierter) Unterausdruck). Backslash-Zahl ist eine back-reference (Rueckverweis).

Das betrifft alles BREs, in EREs (die von awk verwendet werden) gibt es keine Back-References.
Use ed once in a while!

inne
Beiträge: 3273
Registriert: 29.06.2013 17:32:10
Lizenz eigener Beiträge: GNU General Public License
Kontaktdaten:

Re: AWK sub

Beitrag von inne » 08.07.2017 14:09:24

Hm.

Ich habe im Grunde so etwas:

Code: Alles auswählen

programm 2>&1 | awk '
        /hier kommt die Maus/ {
                url=substr($0, 0, match($0, ": ") - 1)
                system(sprintf("fangen %s", url))
        }
'
Wobei "hier kommt die Maus" eine Fehlermeldung nach dem Muster "www.example.net: hier kommt die Maus" von "programm" ist und ich im Skript "fangen" darauf reagiere.

Was ist denn nun die bessere Lösungen. Funktionieren tun ja alle. Aber man will ja gutes AWK lernen ;-) Und wie "genau" müssen reguläre Ausdrücke sein.

Benutzeravatar
Meillo
Moderator
Beiträge: 8782
Registriert: 21.06.2005 14:55:06
Wohnort: Balmora
Kontaktdaten:

Re: AWK sub

Beitrag von Meillo » 08.07.2017 14:25:00

Die Sache ist doch die:

Code: Alles auswählen

substr($0, 0, match($0, ": ") - 1)
ist aequivalent zu:

Code: Alles auswählen

sub(/: .*/, "")
Zweiteres ist kuerzer, klarer und awk-artiger.


Ich wuerde es wohl so machen:

Code: Alles auswählen

programm 2>&1 | awk '
        /hier kommt die Maus/ {
                sub(/: .*/, "")
                print
        }
' | xargs -n 1 fangen
Wobei, eigentlich wuerde ich es so machen:

Code: Alles auswählen

for i in `programm | sed -n '/hier kommt die Maus/s,: .*,,'` ; do
    fangen "$i"
done
(Natuerlich funktioniert das nur mit ``normalen'' Dateinamen, also ohne Whitespace, Newlines und dergleichen.)

(Btw: Willst du eigentlich die Fehlerausgaben verarbeiten, oder warum lenkst du die in die Pipe?)
Use ed once in a while!

inne
Beiträge: 3273
Registriert: 29.06.2013 17:32:10
Lizenz eigener Beiträge: GNU General Public License
Kontaktdaten:

Re: AWK sub

Beitrag von inne » 08.07.2017 14:52:47

(Btw: Willst du eigentlich die Fehlerausgaben verarbeiten, oder warum lenkst du die in die Pipe?)
Ja. Das ist von der Fehlerausgabe stderr.
Ich wuerde es wohl so machen:

Code: Alles auswählen

programm 2>&1 | awk '
        /hier kommt die Maus/ {
                sub(/: .*/, "")
                print
        }
' | xargs -n 1 fangen
Wobei, eigentlich wuerde ich es so machen:

Code: Alles auswählen

for i in `programm | sed -n '/hier kommt die Maus/s,: .*,,'` ; do
    fangen "$i"
done
Ich bin aber jetzt noch auf einen anderen Fehler von mir aufmerksam geworden. Ich muss es so tun wie du oder die Ausgabe von "programm 2>&1" in eine Datei umlenken und dann diese Auswerten (Wenn ich mehr als einen Fall behandeln will). Denn ich kann nicht davon ausgehen das "programm" innerhalb der Pipe beendet ist – richtig? Daran habe ich bei schreiben gar nicht gedacht. Denn "programm" muss beendet sein.
Ich glaub aber dann hab ichs jetzt auch.

Vielen Dank!

Benutzeravatar
Meillo
Moderator
Beiträge: 8782
Registriert: 21.06.2005 14:55:06
Wohnort: Balmora
Kontaktdaten:

Re: AWK sub

Beitrag von Meillo » 08.07.2017 15:22:55

inne hat geschrieben: ↑ zum Beitrag ↑
08.07.2017 14:52:47
Ich bin aber jetzt noch auf einen anderen Fehler von mir aufmerksam geworden. Ich muss es so tun wie du oder die Ausgabe von "programm 2>&1" in eine Datei umlenken und dann diese Auswerten (Wenn ich mehr als einen Fall behandeln will). Denn ich kann nicht davon ausgehen das "programm" innerhalb der Pipe beendet ist – richtig? Daran habe ich bei schreiben gar nicht gedacht. Denn "programm" muss beendet sein.
Die Pipeline laeuft naetuerlich parallel mit, was aber doch eigentlich kein Problem sein sollte (ausser fangen darf erst laufen nachdem programm beendet ist).

Wenn du's nacheinander haben willst, dann so:

Code: Alles auswählen

temp="`mktemp`"
trap "rm -f '$temp'" 0 1 2 3 15
programm 2>"$temp"
<"$temp" awk ... # inkl. Aufruf von fangen
Use ed once in a while!

inne
Beiträge: 3273
Registriert: 29.06.2013 17:32:10
Lizenz eigener Beiträge: GNU General Public License
Kontaktdaten:

Re: AWK sub

Beitrag von inne » 11.07.2017 19:40:38

Ich hab das nun so:

Code: Alles auswählen

awk '
        /hier kommt die Maus/ {
                ...
        }
' <(programm 2>&1)
Das sollte doch auch tun? Also programm ist beendet, bevor awk die Ausgabe verarbeitet?

Da das Skript auf einem Raspberry Pi mit SD-Karte läuft, wollte ich so einen Schreibzugriff einsparen. Gegenüber einer temporären Datei.

Und ein Frage kam mir noch. Kann ich in awk statt // auch wie bei sed ## schreiben, um den RegEx anzugeben? Also ein anders Zeichen dafür nutzen, um nicht escapen zum müssen?

LG,
inne

Benutzeravatar
Meillo
Moderator
Beiträge: 8782
Registriert: 21.06.2005 14:55:06
Wohnort: Balmora
Kontaktdaten:

Re: AWK sub

Beitrag von Meillo » 12.07.2017 06:36:05

inne hat geschrieben: ↑ zum Beitrag ↑
11.07.2017 19:40:38
Da das Skript auf einem Raspberry Pi mit SD-Karte läuft, wollte ich so einen Schreibzugriff einsparen. Gegenüber einer temporären Datei.
Liegt bei dir denn /tmp nicht im Arbeitsspeicher? Das waere jedenfalls empfehlenswert wenn / auf einer SD-Karte ist. Dann legt mktemp(1) seine Datei im Arbeitsspeicher an.
Und ein Frage kam mir noch. Kann ich in awk statt // auch wie bei sed ## schreiben, um den RegEx anzugeben? Also ein anders Zeichen dafür nutzen, um nicht escapen zum müssen?
Nein, das geht nicht. In awk haben die Slashes die Stellung von Anfuehrungszeichen, bloss dass andere Escaperegeln gelten. (Bei sed kannst du sie ja auch nur beim s-Kommando austauschen, hier aber z.B. nicht: sed '/foo/d'.)

Falls es mit dem Escapen der Slashes ganz schlimm waere, kannst du sowas machen:

Code: Alles auswählen

awk '
$0 ~ "http://example.org/a/b/c.html" { print; }
'
Use ed once in a while!

inne
Beiträge: 3273
Registriert: 29.06.2013 17:32:10
Lizenz eigener Beiträge: GNU General Public License
Kontaktdaten:

Re: AWK sub

Beitrag von inne » 12.07.2017 15:17:19

Meillo hat geschrieben: ↑ zum Beitrag ↑
12.07.2017 06:36:05
inne hat geschrieben: ↑ zum Beitrag ↑
11.07.2017 19:40:38
Da das Skript auf einem Raspberry Pi mit SD-Karte läuft, wollte ich so einen Schreibzugriff einsparen. Gegenüber einer temporären Datei.
Liegt bei dir denn /tmp nicht im Arbeitsspeicher? Das waere jedenfalls empfehlenswert wenn / auf einer SD-Karte ist. Dann legt mktemp(1) seine Datei im Arbeitsspeicher an.
Ich denke nicht. Am standard Raspbian habe ich nur die GUI abstellt, Passwort geändert und meine Sachen dann installiert.

Code: Alles auswählen

# mountpoint /tmp/
/tmp/ is not a mountpoint
Also wohl nein.
Wäre aber eine Änderung an der fstab(5) wert.

Antworten