awk フィールドセパレーターが一貫して動作しないのはなぜですか?

awk フィールドセパレーターが一貫して動作しないのはなぜですか?

ss の出力で awk を使用して 4 番目の列を印刷しようとしています。うまくいくこともありますが、列が誤って結合または分割されることもあります。FS のいくつかの異なるオプションを試しましたが、フィールド ヘッダーに 1 つのスペースが含まれているため、ここでは 2 つ以上のスペースになります。

これにより、5 番目の列と空白のヘッダーが表示されます。

$ 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 

ここで同じコマンドを実行すると、4 番目の列が表示されます。これが私が求めているものです。

$ 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

もっと簡単かもしれないとは分かっていますが、さらに処理したいのでcut使用しています。awk

詳細を追加します: ss がこの IPv6 スタイルのアドレスを表示する理由がわかりません。これは私のラップトップから Apache サーバーへの接続ですが、私のラップトップには IPv6 アドレスがありません。

答え1

としてムル示唆されたコメントは、awkおそらく一貫して動作しています。 の出力における間隔は変化する可能性がありますss

ss -nt1 は7 つの列を出力し、各列のヘッダーは、、、、、、、、であることがStateわかりRecv-Qます。4番目と 5 番目の列はコロン ( ) で区切られ、 6 番目と 7 番目のSend-Q列も同様です。他のすべての列はスペース文字で区切られます。 すべての列は、位置合わせに必要なスペースで埋められます。4 番目と 6 番目の列は左側に埋められ、他のすべての列は右側に埋められます。Local AddressPortPeer AddressPort:

さらにパディングが行われる場合があります:

  1. の出力ss -ntが端末に送信される場合は、次のようになります。

    1. 各フィールドの最長コンテンツと最小スペース(6 文字)の合計として計算される行の最小長が端末の幅より小さい場合、すべての列に均等にスペースを埋め込むことで各行を端末の幅まで拡張します。

    2. それ以外の場合、行は折り返され、フィールドは行をまたいで配置されます (上記のように、端末の幅までパディングされます)。

  2. の出力がss -nt端末に送信されない場合(たとえば、パイプで接続されるか、通常のファイルにリダイレクトされる場合)、行の実際の長さは、上で定義された最小の長さよりも大きい 80 の最小倍数として定義されます。すべての列に均等にスペースが埋め込まれ、行全体の長さは 80、160、240、... 文字2になります。

したがって、2 つの列が 2 つ以上のスペースで区切られるという保証はなく、そのシーケンスは分割に対して信頼できません。

ss -tnそれでも、列ヘッダーが既知で固定されており、ヘッダー以外の列にはスペースが含まれていないことに注意すれば、出力をかなり安全な方法で処理できます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
'

これにより、4 列目と 5 列目 (ローカル アドレスとポート) のみがコロンで区切られて出力されます。デフォルトの 1 つのスペース以外のフィールド区切り文字を使用すると、awk7 列ではなく 8 列が識別され、 を実行すると、最後の列の右側に少なくとも 1 つのスペースが埋め込まれた行の末尾に が出力されることに注意してください{ $1=$1; print; }OFS


1その他のオプション (例: -i、、-e)-mは の出力を大幅に変更しますss。簡潔さと明確さのために、このコマンドのみに焦点を当てます。
2近似値であり、不正確な可能性があります。ただし、これはこの質問/回答の要点とは関係ありません。
3どうやらこれは保証されていないため、意図的にすべてのあまり一般的でないケースをカバーしようとはしません。

答え2

awk フィールドセパレーターが一貫して動作しないのはなぜですか?

そうです、信頼できないのは、 の出力内のスペースの数ですss

4列目、これが私が欲しいものです。

次に、ヘッダー ( -H) を削除し、4 番目の列を選択します。

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

ヘッダーは修正されたので、必要に応じて再度追加するだけです。

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

フルコマンド:

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

はい、コンピュータには常に IPv6 アドレス (1 つまたは複数) があります。IPv6 アドレスが必要ない場合は、IPv4 アドレスのみを要求してください。

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

関連情報