![awk フィールドセパレーターが一貫して動作しないのはなぜですか?](https://rvso.com/image/154467/awk%20%E3%83%95%E3%82%A3%E3%83%BC%E3%83%AB%E3%83%89%E3%82%BB%E3%83%91%E3%83%AC%E3%83%BC%E3%82%BF%E3%83%BC%E3%81%8C%E4%B8%80%E8%B2%AB%E3%81%97%E3%81%A6%E5%8B%95%E4%BD%9C%E3%81%97%E3%81%AA%E3%81%84%E3%81%AE%E3%81%AF%E3%81%AA%E3%81%9C%E3%81%A7%E3%81%99%E3%81%8B%3F.png)
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 -nt
1 は7 つの列を出力し、各列のヘッダーは、、、、、、、、であることがState
わかりRecv-Q
ます。4番目と 5 番目の列はコロン ( ) で区切られ、 6 番目と 7 番目のSend-Q
列も同様です。他のすべての列はスペース文字で区切られます。 すべての列は、位置合わせに必要なスペースで埋められます。4 番目と 6 番目の列は左側に埋められ、他のすべての列は右側に埋められます。Local Address
Port
Peer Address
Port
:
さらにパディングが行われる場合があります:
の出力
ss -nt
が端末に送信される場合は、次のようになります。各フィールドの最長コンテンツと最小スペース(6 文字)の合計として計算される行の最小長が端末の幅より小さい場合、すべての列に均等にスペースを埋め込むことで各行を端末の幅まで拡張します。
それ以外の場合、行は折り返され、フィールドは行をまたいで配置されます (上記のように、端末の幅までパディングされます)。
の出力が
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 つのスペース以外のフィールド区切り文字を使用すると、awk
7 列ではなく 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