Construção correta do JSON

Construção correta do JSON

Estou gerando um arquivo JSON com um script de shell, mas não consigo encontrar uma solução para me livrar automaticamente da última vírgula antes do final "}".

Aqui está o meu código:

echo "{" >> out_file
for i in 3 4 5 6 7 #ends at 298
do
y=$i
sommeMag=`awk -F '=' '/'$y'/{sommeMag+=$2}END{print sommeMag}'` "/myfolder/... "
store="mag$y"
if [ -z "$sommeMag" ] #checks is variable is empty
    then
        echo  "\"" $store"\"":0",">> out_file
    else
        echo "\""$store"\"":$sommeMag"," >> out_file
    fi
done
echo "}" >> out_file

O arquivo está terminando assim:

{
" mag297":0,
" mag298":0, <-- syntaxt error
}

O arquivo deve terminar assim:

{
...
" mag297":0,
" mag298":0 <-- no more comma
}

Como posso gerenciar isso?

O código foi editado para ser mais legível aqui.

Responder1

O que você deveria fazer é imprimir o primeiro item e deixar o cursor parar. Então você pode começar a trabalhar no loop e avançar o cursor inserindo vírgula e nova linha. printfé mais útil do que echo nesse sentido. Para evitar repetições excessivas e encurtar o código, use funções.

Como não tenho os arquivos que você está analisando com o awk, não posso trabalhar com seu script, mas o exemplo abaixo ilustra o que estou tentando transmitir.

#!/bin/bash

print_entry(){
    # sommeMag would be variable $2, $1 is store
    if [ -z $2 ]
    then
        printf '"%s":0' "$1" 
    else
        printf '"%s":%s' "$1" "$2"
    fi
}

# main part of the script
printf "%s\n" "{"

# do the first one unconditionally but without comma
print_entry "something1" "something2"
for i in 2 3 4 5
do
    # because first one was printed unconditionally
    # now we just keep printing comma and moving cursor
    # to newline to insert the new entry
    printf ",\n"
    print_entry "something$i" "$i"
done
printf "\n%s"  "}"

Execução de amostra

$ ./make_json.sh                                                                                                                                      
{
"something1":something2,
"something2":2,
"something3":3,
"something4":4,
"something5":5
}$ 

A abordagem acima é normalmente o que chamo de "adicionar vírgula atrás". Outra abordagem seria "Adicionar vírgula à frente", mas em vez de loop for, use whileloop com contador para simular o comportamento do loop for no estilo C. Se você chegar ao último item, não digite vírgula. Basicamente

counter=1
while [ $counter -le 298 ]
do
    $json_value=$(command1)
    if [ $counter -ne 298  ]
    then
        echo $json_value ,
    else
        echo $json_value
    fi 
    $counter=$(($counter+1))
done

Responder2

#
# var initialzizations
#
# newline
NL=`printf '\nn'`;NL=${NL%?}

# double quote
q=\"

# separator set to a comma+newline
sep=",$NL"

# container for the json result
json=;

# begin/end of count values
start=3
 stop=7

# looping
# in case seq utility not found, then you can use: yes | sed -ne "$start,$stop=;${stop}q"
for i in `seq "$start" "$stop"`
do
   y=$i
   sommeMag=`awk -F = "/$y/"'{sommeMag+=$2}END{print sommeMag}'` "/myfolder/... "
   store=mag$y
   json=${json:-}${json:+"$sep"}$q\ $store$q:${sommeMag:-0}
done

# remove the separtor from the end and also place a newline
json=${json%"$sep"}$NL

printf '{\n%s}\n' "$json" >> out_file

Responder3

Dado um fluxo de pares de chaves e valores delimitados por tabulações, construa um único objeto JSON contendo essas chaves e valores. (Se os dados contiverem tabulações reais, basta usar um caractere que não faça parte dos dados.)

for something something
do
    output key and value with a tab in-between
done |
jq -Rn 'reduce inputs as $line ({}; ($line|split("\t")) as [$key, $value] | . += {($key): $value})'

É usado jqpara ler um fluxo de linhas, cada uma com uma chave e um valor separados por um caractere de tabulação. Cada linha é lida e usada como $linena jqexpressão. Ele é dividido no caractere de tabulação e a chave e o valor são adicionados ao objeto que a reduceinstrução está acumulando. O objeto cresce à medida que novas chaves e valores são adicionados e, quando o fluxo de entrada termina, o objeto finalizado é gerado.

Esta é uma versão bem impressa da jqexpressão:

reduce inputs as $line ({}; 
    ($line | split("\t")) as [$key, $value] | 
    . += {($key): $value}
)

Um script de exemplo funcional:

#!/bin/bash

for (( i = 1; i <= 10; ++i ))
do
        printf 'key%.2d\tvalue%.2d\n' "$i" "$i"
done |
jq -Rn 'reduce inputs as $line ({}; ($line|split("\t")) as [$key, $value] | . += {($key): $value})'

Saída:

{
  "key01": "value01",
  "key02": "value02",
  "key03": "value03",
  "key04": "value04",
  "key05": "value05",
  "key06": "value06",
  "key07": "value07",
  "key08": "value08",
  "key09": "value09",
  "key10": "value10"
}

Se não houver muitas chaves e valores, podemos simplificar isso e apenas criar uma jqlinha de comando muito longa que associa cada chave ao seu valor usando --arge, em seguida, solicitar jqa saída destes (que serão encontrados na $ARGS.namedestrutura interna):

#!/bin/bash

args=()
for (( i = 1; i <= 10; ++i ))
do
        printf -v key   'key%.2d'   "$i"
        printf -v value 'value%.2d' "$i"
        args+=( --arg "$key" "$value" )
done

jq -n "${args[@]}" '$ARGS.named'

Neste caso, a saída será idêntica à saída do script anterior. A diferença é que o número de chaves e valores com os quais o script anterior pode lidar é limitado pela memória disponível para o jqprocesso. Em contraste, esta variante é limitada pelo comprimento máximo permitido da linha de comando, que geralmente é muito menor. Por outro lado, este script mais recente irá lidar com valores que contenham qualquer caractere, incluindo novas linhas.

informação relacionada