Warum funktioniert der AWK-Feldtrenner nicht konsistent?

Warum funktioniert der AWK-Feldtrenner nicht konsistent?

Ich versuche, awk mit der Ausgabe von ss zu verwenden, um die vierte Spalte zu drucken. Manchmal funktioniert es, aber manchmal werden die Spalten falsch zusammengeführt oder geteilt. Ich habe ein paar verschiedene Optionen für FS ausprobiert, hier sind es zwei oder mehr Leerzeichen, weil die Feldüberschriften ein einzelnes Leerzeichen enthalten.

Dadurch erhalte ich die fünfte Spalte und eine leere Kopfzeile:

$ ss -tn
State   Recv-Q    Send-Q                Local Address:Port                   Peer Address:Port     
ESTAB   0         36                     172.31.19.34:22                   172.115.128.85:64478    
ESTAB   0         0             [::ffff:172.31.19.34]:80          [::ffff:172.115.128.85]:65446    


$ ss -tn | awk -F '[[:space:]][[:space:]]+' '{print $4}'

172.115.128.86:64478 
[::ffff:172.115.128.86]:65446 

Derselbe Befehl gibt mir hier die vierte Spalte, das ist, was ich will.

$ ss -tn
State     Recv-Q      Send-Q              Local Address:Port               Peer Address:Port       
ESTAB     0           36                   172.31.19.34:22               172.115.128.85:64478   

$ ss -tn | awk -F '[[:space:]][[:space:]]+' '{print $4}'
Local Address:Port
172.31.19.34:22

Ich weiß, cutdass es vielleicht einfacher ist, aber ich verwende es awk, weil ich weitere Verarbeitungen durchführen möchte.

Um es genauer zu beschreiben: Ich bin nicht sicher, warum SS diese IPv6-Adresse anzeigt. Dies ist eine Verbindung von meinem Laptop zum Apache-Server, aber mein Laptop hat keine IPv6-Adresse.

Antwort1

AlsMuruangedeutet in einemKommentar, awkfunktioniert wahrscheinlich konsistent. Was variieren kann, ist der Abstand in der Ausgabe von ss.

Es stellt sich heraus, dass ss -nt1 sieben Spalten ausgibt, deren Überschriften lauten: State, Recv-Q, Send-Q, Local Address, Port, Peer Address, Port. Die vierte und fünfte Spalte sind durch einen Doppelpunkt ( :) getrennt; dasselbe gilt für die sechste und siebte. Alle anderen sind durch ein Leerzeichen getrennt.
Alle Spalten sind bei Bedarf mit Leerzeichen aufgefüllt, um die Ausrichtung zu gewährleisten. Die vierte und sechste sind links aufgefüllt, alle anderen rechts.

Weitere Auffüllungen können erfolgen:

  1. Wenn die Ausgabe ss -ntan ein Terminal geleitet wird:

    1. Wenn die Mindestlänge der Zeilen, berechnet als Summe des längsten Inhalts jedes Felds plus des Mindestabstands (sechs Zeichen), kleiner als die Breite des Terminals ist, wird jede Zeile auf die Breite des Terminals erweitert, indem alle Spalten gleichmäßig mit Leerzeichen aufgefüllt werden.

    2. andernfalls werden Zeilen umbrochen und Felder zeilenübergreifend ausgerichtet (aufgefüllt wie oben, bis zur Breite des Terminals).

  2. Wenn die Ausgabe ss -ntnicht an ein Terminal geleitet wird (z. B. an eine normale Datei weitergeleitet wird), wird die tatsächliche Länge der Zeilen als das minimale Vielfache von 80 definiert, das höher ist als die oben definierte Mindestlänge. Alle Spalten werden gleichmäßig mit Leerzeichen aufgefüllt, um eine Gesamtzeilenlänge von 80, 160, 240, ... Zeichen 2 zu erreichen .

Daher gibt es keine Garantie dafür, dass zwei Spalten durch zwei oder mehr Leerzeichen getrennt sind, was diese Sequenz für die Aufteilung unzuverlässig macht.

Sie können die Ausgabe von dennoch ss -tneinigermaßen sicher verarbeiten, sofern Sie beachten, dass die Spaltenüberschriften bekannt und festgelegt sind und dass außer den Überschriften keine der Spalten Leerzeichen enthalten sollte 3 :

ss -nt | sed '
  1 s/[ ]Address:/_Address|/g           # Remove the known spaces from column
                                        # headers; also, change ":" into "|"
  s/:\([^:|]*[ ]\)/|\1/g                # Change the colons used as separators
                                        # into vertical bars "|", to avoid
  s/:\([^:|]*\)$/|\1/g                  # confusion with those in IPv6s
' | awk -v FS='\\||[ ]+' -v OFS=":" '   # Split on sequences of one or more
  { print $4,$5 }                       # spaces OR on any vertical bar
'

Dadurch werden nur die vierte und fünfte Spalte (lokale Adresse und Port) gedruckt, getrennt durch einen Doppelpunkt. Beachten Sie, dass bei Verwendung eines Feldtrennzeichens, das nicht das standardmäßige einzelne Leerzeichen ist, awkacht statt sieben Spalten identifiziert werden und dass, wenn Sie ein verwenden { $1=$1; print; }, am Ende jeder Zeile ein gedruckt wird, OFSbei der die letzte Spalte rechts mit mindestens einem Leerzeichen aufgefüllt ist.


1 Andere Optionen (z. B. -i, -e, -m) ändern die Ausgabe von drastisch ss. Der Kürze und Klarheit halber konzentrieren wir uns nur auf diesen genauen Befehl.
2 Ungefähr und möglicherweise ungenau. Dies ist jedoch für den Inhalt dieser Frage/Antwort nicht relevant.
3 Dies ist offensichtlich nicht garantiert, wir werden absichtlich nicht versuchen, alle weniger üblichen Fälle abzudecken.

Antwort2

Warum funktioniert der AWK-Feldtrenner nicht konsistent?

Unzuverlässig ist die Anzahl der Leerzeichen in der Ausgabe von ss.

die vierte Spalte, das ist, was ich will.

Anschließend einfach die Überschrift ( -H) entfernen und die vierte Spalte auswählen:

$ ss -taH | awk '{print $4}'
172.31.19.34:22
[::ffff:172.31.19.34]:80

Da die Kopfzeile korrigiert ist, fügen Sie sie (falls erforderlich) einfach wieder hinzu:

$  echo "Local Address:Port"
Local Address:Port

Vollständiger Befehl:

$ echo "Local Address:Port"; ss -tnH | awk '{print $4}'
Local Address:Port
172.31.19.34:22
[::ffff:172.31.19.34]:80

Ja, Ihr Computer hat immer IPv6-Adressen (eine oder mehrere). Wenn Sie diese nicht möchten, fordern Sie nur die IPv4-Adressen an:

$ ss -tnH4 | awk '{print $4}'
172.31.19.34:22

verwandte Informationen