Por que o separador de campos awk não está funcionando de forma consistente?

Por que o separador de campos awk não está funcionando de forma consistente?

Estou tentando usar o awk com a saída de ss para imprimir a quarta coluna. Às vezes funciona, mas outras vezes mescla ou divide as colunas incorretamente. Eu tentei algumas opções diferentes para FS, aqui são dois ou mais espaços porque os cabeçalhos dos campos contêm um único espaço.

Isso está me dando a quinta coluna e um cabeçalho em branco:

$ 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 

O mesmo comando aqui está me dando a quarta coluna, é isso que eu quero.

$ 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

Eu sei cutque pode ser mais fácil, mas estou usando awkporque quero fazer mais processamento.

Para adicionar detalhes: não sei por que ss está mostrando esse endereço estilo IPv6. Esta é uma conexão do meu laptop ao servidor Apache, mas meu laptop não possui um endereço IPv6.

Responder1

Comomurusugerido em umComente, awkprovavelmente está funcionando de forma consistente. O que pode variar é o espaçamento na saída do arquivo ss.

Acontece que ss -nt1 gera sete colunas, cujos cabeçalhos são: State, Recv-Q, Send-Q, Local Address, Port, Peer Address, Port. A quarta e a quinta colunas são separadas por dois pontos ( :); a mesma coisa para o sexto e o sétimo. Todos os outros são separados por um caractere de espaço.
Todas as colunas são preenchidas com espaços quando necessário para alinhamento. O quarto e o sexto são preenchidos à esquerda, todos os outros à direita.

Mais preenchimento pode acontecer:

  1. Se a saída de ss -ntfor direcionada para um terminal:

    1. se o comprimento mínimo de suas linhas, calculado como a soma do conteúdo mais longo de cada campo mais o espaçamento mínimo (seis caracteres), for menor que a largura do terminal, cada linha será expandida até a largura do terminal por preenchimento uniforme todas as colunas com espaços;

    2. caso contrário, as linhas serão quebradas e os campos serão alinhados entre as linhas (preenchidos como acima, até a largura do terminal).

  2. Se a saída de ss -ntnão for direcionada para um terminal (por exemplo, for canalizada ou redirecionada para um arquivo normal), o comprimento real das linhas será definido como o múltiplo mínimo de 80 que é maior que o comprimento mínimo definido acima. Todas as colunas são preenchidas uniformemente com espaços para atingir um comprimento total de linha que será consequentemente de 80, 160, 240, ... caracteres 2 .

Assim, não há garantia de que duas colunas serão separadas por dois ou mais espaços, tornando essa sequência não confiável para divisão.

No entanto, você pode lidar com a saída de ss -tnuma maneira razoavelmente segura, observando que os cabeçalhos das colunas são conhecidos e fixos e que, exceto os cabeçalhos, nenhuma de suas colunas deve incluir espaços 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
'

Isso imprimirá apenas a quarta e a quinta colunas (endereço local e porta), separadas por dois pontos. Observe que, usando um separador de campo que não seja o espaço único padrão, awkidentificará oito colunas em vez de sete e, se você fizer um { $1=$1; print; }, imprimirá um OFSno final de qualquer linha para a qual a última coluna seja preenchida à direita com pelo pelo menos um espaço.


1 Outras opções (por exemplo -i, -e, -m) alteram drasticamente a saída de ss. Por questões de brevidade e clareza, focaremos apenas neste comando exato.
2 Aproximado e possivelmente inexato. Mas isso não é relevante para o ponto desta pergunta/resposta.
3 Aparentemente isto não é garantido, deliberadamente não tentaremos cobrir todos os casos menos habituais.

Responder2

Por que o separador de campos awk não está funcionando de forma consistente?

É, o que não é confiável é o número de espaços na saída de ss.

a quarta coluna, é isso que eu quero.

Depois é só remover o cabeçalho ( -H) e selecionar a quarta coluna:

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

Como o cabeçalho está fixo, basta adicioná-lo novamente (se necessário):

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

Comando completo:

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

Sim, seu computador sempre possui endereços IPv6 (um ou vários). Caso não os queira, peça apenas os endereços IPv4:

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

informação relacionada