analisa pares de valores separados por dois pontos

4

Eu escrevi um script que converte nmcli --mode multiline dev wifi em json, mas estou achando que é inconsistente (quebra quando os resultados têm um espaço) , longo e difícil de ler.

Gostaria de saber se é possível canalizar os resultados diretamente para jq . A saída se parece com:

*:                                       
SSID:                                   VIDEOTRON2255
MODE:                                   Infra
CHAN:                                   11
RATE:                                   54 Mbit/s
SIGNAL:                                 69
BARS:                                   ▂▄▆_
SECURITY:                               WPA1 WPA2
*:                                      * 
SSID:                                   VIDEOTRON2947
MODE:                                   Infra
CHAN:                                   6
RATE:                                   54 Mbit/s
SIGNAL:                                 49
BARS:                                   ▂▄__
SECURITY:                               WPA1 WPA2

Estou procurando gerar algo assim:

[{
    "network": "VIDEOTRON2255",
    "mode": "Infra",
    "chan": "11",
    "rate": "54 Mbit/s",
    "signal": "69",
    "bars": "▂▄▆_",
    "security": "WPA1 WPA2"
},
{
    "network": "VIDEOTRON2947",
    "mode": "Infra",
    "chan": "6",
    "rate": "54 Mbit/s",
    "signal": "49",
    "bars": "▂▄__",
    "security": "WPA1 WPA2"
}]

Sim, eu fiz uma pergunta relacionada mais cedo. Este é o primeiro script que eu escrevi baseado em Resposta do Gilles , funcionou para alguns dos valores, mas não para security ou rate que possuem espaços.

    
por Philip Kirkbride 19.10.2017 / 23:33

5 respostas

5

O script ao qual você está vinculado é extremamente ineficiente - você está fazendo muitos pré-processamentos inúteis ...
Use nmcli no modo --terse , pois, de acordo com o manual, "este modo é projetado e adequado para processamento de computador (script)" , especifica os campos desejados e canaliza a saída para jq -sR , por exemplo

printf '%s' "$(nmcli -f ssid,mode,chan,rate,signal,bars,security -t dev wifi)" | \
jq -sR 'split("\n") | map(split(":")) | map({"network": .[0],
                                             "mode": .[1],
                                             "channel": .[2],
                                             "rate": .[3],
                                             "signal": .[4],
                                             "bars": .[5],
                                             "security": .[6]})'
    
por 22.10.2017 / 15:34
2

Este código GNU sed não é jq , (não é uma conversão complexa), mas parece funcionar bem o suficiente, (mesmo as barras sair OK):

nmcli --mode multiline dev wifi | 
sed    '/^*/! {s/^[A-Z]*/\L&/
               s/ssid/network/
               s/: */": "/
               s/$/"/
               {/^sec/!s/$/,/}
               s/^/\t"/}
        1     s/^\*.*/[{/
        /^\*/ s/.*/},\n{/
        $  {p;s/.*/}]/}'

Mais fácil de ler o script standalone pcsvp.sed , (salve no arquivo e execute chmod +x pcsvp.sed ):

#!/bin/sed -f
# Text lines (the non "*:" lines.)
/^*/! {s/^[A-Z]*/\L&/
       s/ssid/network/
       s/: */": "/
       s/$/"/
       {/^sec/!s/$/,/}
       s/^/\t"/}

# First JSON line
1     s/^\*.*/[{/

# Middle JSON lines.  If a line begins with a '*'...
/^\*/ s/.*/},\n{/

# Last line, close up the JSON.
$     {p;s/.*/}]/}

Para executar isso:

nmcli --mode multiline dev wifi | ./pcsvp.sed

Observação: como existem dúvidas sobre o arquivo de entrada , optei por usar nmcli para entrada. Na minha localização, isso mostra cerca de 50 redes, o que torna a saída resultante muito longa para citar aqui.

Se o erro de digitação da amostra de entrada for corrigido, ./pcsvp.sed input.txt retornará:

[{
    "network": "VIDEOTRON2255",
    "mode": "Infra",
    "chan": "11",
    "rate": "54 Mbit/s",
    "signal": "69",
    "bars": "▂▄▆_",
    "security": "WPA1 WPA2"
},
{
    "network": "VIDEOTRON2947",
    "mode": "Infra",
    "chan": "6",
    "rate": "54 Mbit/s",
    "signal": "49",
    "bars": "▂▄__",
    "security": "WEP"
}]
    
por 22.10.2017 / 09:47
1

Solução jq complexa (com BARS linha removida, pois contém caracteres irregulares / não-ASCII):

Arquivo de entrada input.txt :

*:                                       
SSID:                                   VIDEOTRON2255
MODE:                                   Infra
CHAN:                                   11
RATE:                                   54 Mbit/s
SIGNAL:                                 69
SECURITY:                               WPA1 WPA2
*:                                      * 
SSID:                                   VIDEOTRON2947
MODE:                                   Infra
CHAN:                                   6
RATE:                                   54 Mbit/s
SIGNAL:                                 49
SECURITY:                               WPA1 WPA2

O trabalho:

jq -sR '[ gsub("[*]: *\n| {2,}";"") | gsub("SSID";"network") | split("\n[*]:[*] +\n";"n")[] 
    | [ capture("(?<key>[^:\n]+):(?<value>[^:\n]+)";"g") | .key |= (. | ascii_downcase) ] 
    | from_entries ]' input.txt

A saída:

[
  {
    "network": "VIDEOTRON2255",
    "mode": "Infra",
    "chan": "11",
    "rate": "54 Mbit/s",
    "signal": "69",
    "security": "WPA1 WPA2"
  },
  {
    "network": "VIDEOTRON2947",
    "mode": "Infra",
    "chan": "6",
    "rate": "54 Mbit/s",
    "signal": "49",
    "security": "WPA1 WPA2"
  }
]

Abordagem adicional para outra entrada específica apresentada / postada no link :

jq -sR '[sub("[*]: *[*]\n";"") | gsub(" {2,}";"") | gsub("SSID";"network") 
  | split("\n[*]: *\n";"n")[] 
  | [ capture("(?<key>[^:\n]+):(?<value>[^:\n]+)";"g") | .key |= (. | ascii_downcase) ] 
  | from_entries]' input.txt
    
por 20.10.2017 / 01:01
1

Se você puder, use uma ferramenta que entenda o JSON para frente e para trás. Eu usaria o Python:

#! /usr/bin/env python3
import json
import re
import sys

objects = []
obj = {}
for line in sys.stdin:
    entry = re.split(':\s*', line.strip(), maxsplit=1) # split on first ':'
    if entry[0] == '*':
        if obj:  # skip a null entry (the first, here)
            obj['network'] = obj.pop('ssid') # rename the SSID entry
            objects.append(obj)
        obj = {} # start a new object for each '*'
        continue
    obj[entry[0].lower()] = entry[1]  # lowercase the key
objects.append(obj)
json.dump(objects, sys.stdout)

Me dá:

[{"mode": "Infra", "chan": "11", "rate": "54 Mbit/s", "signal": "69", "bars": "\u2582\u2584\u2586_", "security": "WPA1 WPA2", "network": "VIDEOTRON2255"}, {"ssid": "VIDEOTRON2947", "mode": "Infra", "chan": "6", "rate": "54 Mbit/s", "signal": "49", "bars": "\u2582\u2584__", "security": "WPA1 WPA2"}]

que, quando bastante impressa por jq , é:

[
  {
    "mode": "Infra",
    "chan": "11",
    "rate": "54 Mbit/s",
    "signal": "69",
    "bars": "▂▄▆_",
    "security": "WPA1 WPA2",
    "network": "VIDEOTRON2255"
  },
  {
    "ssid": "VIDEOTRON2947",
    "mode": "Infra",
    "chan": "6",
    "rate": "54 Mbit/s",
    "signal": "49",
    "bars": "▂▄__",
    "security": "WPA1 WPA2"
  }
]
    
por 22.10.2017 / 19:05
1
  1. GNU sed e mlr método:

    nmcli dev wifi | sed 'y/*/ /;1{s/.*/\L&/;s/ssid/network/};s/   */\t/g'  | 
    mlr --p2j --fs '\t' --jvstack --jlistwrap cat
    
  2. bash , modo de texto nmcli , (digitada da resposta de don_chrissti ), e mlr :

    h=ssid:mode:chan:rate:signal:bars:security
    { echo ${h/ssid/network} ; nmcli -f ${h//:/,} -t dev wifi ; } | \
    mlr --c2j --fs ':' --jvstack --jlistwrap cat
    
por 22.10.2017 / 19:45