Werfen wir einen Blick auf die Manpage von GNU awk:
FS — Das Eingabefeld-Trennzeichen, standardmäßig ein Leerzeichen. Siehe Felder , oben.
Zu den Feldern Abschnitt!
Während jeder Eingabedatensatz gelesen wird, teilt gawk den Datensatz unter Verwendung des Werts von FS in Felder auf Variable als Feldtrenner. Wenn FS ein einzelnes Zeichen ist, werden Felder durch dieses Zeichen getrennt. Wenn FS die Nullzeichenfolge ist, dann wird jedes einzelne Zeichen zu einem separaten Feld. Andernfalls FS wird als vollständiger regulärer Ausdruck erwartet. Im speziellen Fall, dass FS ein einzelnes Leerzeichen ist, Felder werden durch Reihen von Leerzeichen und/oder Tabulatoren und/oder Zeilenumbrüchen getrennt.
Hier ist eine pragmatische Zusammenfassung das gilt für alle wichtigen Awk-Implementierungen :
- GNU Awk (
gawk) - der Standardwertawkin einigen Linux-Distributionen - Mawk (
mawk) - der Standardwertawkin einigen Linux-Distributionen (z. B. frühere Versionen von Ubuntu) - BWK Awk - der Standard
awkauf BSD-ähnlichen Plattformen, einschließlich macOS
Aktuelle Versionen von all diese Implementierungen folgen dem POSIX-Standard in Bezug auf field Trennzeichen (aber nicht aufzeichnen Trennzeichen).
Glossar:
-
RSist der Eingabe-Datensatz Trennzeichen , die beschreibt, wie die Eingabe in Datensätze aufgeteilt wird :- Der von POSIX vorgegebene Standardwert ist ein Zeilenumbruch , auch als
\nbezeichnet unter; das heißt, die Eingabe wird in Zeilen aufgeteilt standardmäßig . - Am
awkBefehlszeile vonRSkann als-v RS=<sep>angegeben werden . - POSIX beschränkt
RSzu einem Literal, Einzelzeichen Wert, aber GNU Awk und Mawk unterstützen mehrere Zeichen Werte, die erweiterte reguläre Ausdrücke sein können (BWK Awk tut nicht unterstütze das).
- Der von POSIX vorgegebene Standardwert ist ein Zeilenumbruch , auch als
-
FSist das Eingabe-Feld Trennzeichen , die beschreibt, wie jeder Datensatz ist in Felder aufgeteilt ; es kann sich um einen erweiterten regulären Ausdruck handeln .- Am
awkBefehlszeile vonFSkann als-F <sep>angegeben werden (oder-v FS=<sep>). - Der von POSIX vorgeschriebene Standardwert ist formal ein Leerzeichen (
0x20), aber dieses Leerzeichen ist nicht buchstäblich als (einziges) Trennzeichen interpretiert, hat aber besondere Bedeutung ; siehe unten.
- Am
Standardmäßig :
- beliebiger Lauf von Leerzeichen und/oder Tabs und/oder Zeilenumbrüche wird als Feldtrennzeichen behandelt
- wobei führende und nachlaufende Läufe ignoriert werden .
Beachten Sie das mit dem standardmäßigen Trennzeichen für Eingabedatensätze (RS ), \n , Zeilenumbrüche normalerweise Geben Sie das Bild nicht als Feldtrenner ein , weil kein Datensatz selbst enthält \n in diesem Fall.
Zeilenumbrüche als Feldtrenner tun ins Spiel kommen , jedoch:
- Wenn
RSauf einen Wert gesetzt, der zu Datensätzen selbst führt enthält\nInstanzen (z. B. wennRSwird auf die leere Zeichenfolge gesetzt; siehe unten). - Allgemein , wenn der
split()Die Funktion wird verwendet, um einen String ohne explizites Feldtrennargument in Array-Elemente aufzuteilen.- Obwohl die Eingabedatensätze enthält nicht
\nInstanzen im Fall der VorgabeRSIn Kraft ist dersplit()Funktion, wenn sie ohne ein explizites Feldtrenner-Argument für eine mehrzeilige Zeichenfolge aus einer anderen Quelle aufgerufen wird (z. B. eine Variable, die über-vübergeben wird Option oder als Pseudo-Dateiname) immer behandelt\nals Feldtrenner.
- Obwohl die Eingabedatensätze enthält nicht
Wichtige NICHT standardmäßige Überlegungen :
-
Zuweisen des leeren Zeichenfolge zu
RShat eine besondere Bedeutung :Es liest die Eingabe im Absatzmodus , was bedeutet, dass die Eingabe durch Läufe von nicht leeren Zeilen in Datensätze aufgeteilt wird , wobei führende und nachfolgende Leerzeilen ignoriert werden . -
Wenn Sie etwas anderes zuweisen als ein Literal Leerzeichen zu
FS, die Interpretation vonFSändert sich grundlegend :- Eine Single Zeichen oder jedes Zeichen aus einem bestimmten Zeichensatz wird individuell anerkannt als Feldtrenner - nicht läuft davon, wie bei der Voreinstellung.
- Zum Beispiel das Setzen von
FSbis[ ]- obwohl es effektiv entspricht einem einzigen Leerzeichen - bewirkt jedes individuelle Leerzeichen in jedem Datensatz, der als Feldtrennzeichen behandelt werden soll. - Um Läufe zu erkennen , der Regex-Quantifizierer (Duplizierungssymbol)
+muss benutzt werden; B.[\t]+würde Läufe erkennen von Tabulatoren als einzelnes Trennzeichen.
- Zum Beispiel das Setzen von
- Führend und nachlaufend Trennzeichen werden NICHT ignoriert , und trennen Sie stattdessen leer Felder.
- Einstellung
FSzum leeren String bedeutet, dass jedes Zeichen eines Datensatzes ist sein eigenes Feld .
- Eine Single Zeichen oder jedes Zeichen aus einem bestimmten Zeichensatz wird individuell anerkannt als Feldtrenner - nicht läuft davon, wie bei der Voreinstellung.
-
Wie von POSIX vorgeschrieben, wenn
RSwird auf die leere Zeichenfolge gesetzt (Absatzmodus), Zeilenumbrüche (\n) sind auch gelten als Feldtrenner , unabhängig vom Wert vonFS.
- Mit
-Pin Kraft undRSauf den leeren String setzen ,\nist noch als Feldtrenner behandelt:
gawk -P -F' ' -v RS='' '{ printf "<%s>, <%s>\n", $1, $2 }' <<< $'a\nb' - Mit
-Pin Kraft und ein nicht leererRS,\nwird NICHT als Feldtrenner behandelt - das ist das obsolete Verhalten:
gawk -P -F' ' -v RS='|' '{ printf "<%s>, <%s>\n", $1, $2 }' <<< $'a\nb'
Ein Fix kommt bald , laut den Betreuern von GNU Awk; erwarten Sie es in Version 4.2 (kein Zeitrahmen angegeben).
(Dank an @JohnKugelman und @EdMorton für ihre Hilfe.)
'[ ]+' funktioniert bei mir. Führen Sie awk -W version aus um die awk-Version zu erhalten. Meine ist GNU Awk 4.0.2 .
# cat a.txt
tcp 0 0 10.192.25.199:65002 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:26895 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:111 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:18422 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN
tcp 0 0 10.192.25.199:8888 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:50010 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:50075 0.0.0.0:* LISTEN
tcp 0 0 10.192.25.199:8093 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:8670 0.0.0.0:* LISTEN
Zum Beispiel möchte ich den Listen-Port erhalten. Also muss ich das awk-Standardtrennzeichen verwenden, das mit ':'
hinzugefügt wurde# cat a.txt | awk -F '[ ]+|:' '{print $5}'
65002
26895
111
18422
22
8888
50010
50075
8093
8670
Wenn Sie nur das Standardtrennzeichen testen möchten, können Sie
ausführen# cat a.txt | awk -F '[ ]+' '{print $4}'
10.192.25.199:65002
127.0.0.1:26895
0.0.0.0:111
0.0.0.0:18422
0.0.0.0:22
10.192.25.199:8888
0.0.0.0:50010
0.0.0.0:50075
10.192.25.199:8093
0.0.0.0:8670
Das Ergebnis ist wie erwartet.
Die Frage the default delimiter is only space for awk? ist mehrdeutig, aber ich werde versuchen, beide Fragen zu beantworten, die Sie vielleicht stellen.
Der Standardwert von FS Variable (die das Feldtrennzeichen enthält, das awk mitteilt, wie Datensätze beim Lesen in Felder getrennt werden sollen) ist ein einzelnes Leerzeichen.
Das, was awk verwendet, um Datensätze in Felder zu trennen, ist ein "Feldtrennzeichen", das ein regulärer Ausdruck mit einigen zusätzlichen Funktionen ist, die nur gelten, wenn das Feldtrennzeichen ein einzelnes Leerzeichen ist. Diese zusätzliche Funktionalität ist die:
- Führende und nachgestellte Leerzeichen werden bei der Feldaufteilung ignoriert.
- Felder werden durch Ketten von zusammenhängenden Leerzeichen getrennt, die Leerzeichen, Tabulatoren und Zeilenumbrüche enthalten.
- Wenn Sie ein wörtliches Leerzeichen als Feldtrennzeichen verwenden möchten, müssen Sie es als
[ ]angeben Anstatt nur ein eigenständiges literales Leerzeichen, wie Sie es in einem regulären Ausdruck könnten.
Zusätzlich dazu, dass Feldtrennzeichen verwendet werden, um Datensätze beim Lesen der Eingabe in Felder aufzuteilen, werden sie in einigen anderen Kontexten verwendet, z. das 3. Argument für split() , daher ist es wichtig, dass Sie wissen, welche Kontexte einen String, einen regulären Ausdruck oder einen Fieldsep erfordern, und die Manpage gibt dies jeweils klar an.
Obiges erklärt unter anderem dies:
$ echo ' a b c ' | awk '{printf "%d: <%s> <%s> <%s>\n", NF, $1, $2, $3}'
3: <a> <b> <c>
$ echo ' a b c ' | awk -F' ' '{printf "%d: <%s> <%s> <%s>\n", NF, $1, $2, $3}'
3: <a> <b> <c>
$ echo ' a b c ' | awk -F'[ ]' '{printf "%d: <%s> <%s> <%s>\n", NF, $1, $2, $3}'
5: <> <a> <b>
Wenn Sie also nicht verstehen, warum die ersten 2 dieselbe Ausgabe erzeugen, die letzte jedoch anders ist, fragen Sie bitte.