Construcción correcta JSON

Construcción correcta JSON

Estoy generando un archivo JSON con un script de shell pero no puedo encontrar una solución para eliminar automáticamente la última coma justo antes del final "}".

Aquí está mi 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

El archivo termina de esta manera:

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

El archivo debería terminar de esta manera:

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

¿Cómo puedo gestionar eso?

El código ha sido editado para que sea más legible aquí.

Respuesta1

Lo que deberías hacer es imprimir el primer elemento y dejar que el cursor se detenga. Luego puede comenzar a trabajar en el bucle y avanzar el cursor insertando una coma y una nueva línea. printfes más útil que el eco en ese sentido. Para evitar repeticiones excesivas y acortar el código, utilice funciones.

Como no tengo los archivos que estás analizando con awk, no puedo trabajar con tu script, pero el siguiente ejemplo ilustra lo que intento 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"  "}"

Ejecución de muestra

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

El enfoque anterior suele ser lo que yo llamo "agregar una coma detrás". Otro enfoque sería "Agregar coma adelante", pero en lugar de un bucle for, use whileun bucle con contador para simular el comportamiento del bucle for estilo C. Si llega al último elemento, no escriba una coma. Básicamente

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

Respuesta2

#
# 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

Respuesta3

Dado un flujo de pares de clave y valor delimitados por tabulaciones, construya un único objeto JSON que contenga esas claves y valores. (Si los datos contienen pestañas reales, simplemente use un carácter que no forme parte de los datos).

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})'

Esto se utiliza jqpara leer un flujo de líneas, cada una con una clave y un valor separados por un carácter de tabulación. Cada línea se lee y utiliza como $lineen la jqexpresión. Se divide en el carácter de tabulación y la clave y el valor se agregan al objeto que reduceestá acumulando la declaración. El objeto crece a medida que se agregan nuevas claves y valores, y cuando finaliza el flujo de entrada, se genera el objeto terminado.

Esta es una versión bastante impresa de la jqexpresión:

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

Un script de ejemplo 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})'

Producción:

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

Si no hay demasiadas claves y valores, podemos simplificar esto y simplemente crear una jqlínea de comando muy larga que asocie cada clave con su valor usando --argy luego solicitar jqque se generen estos (que se encontrarán en la $ARGS.namedestructura 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'

En este caso, el resultado será idéntico al resultado del script anterior. La diferencia es que la cantidad de claves y valores que puede manejar el script anterior está limitada por la memoria disponible para el jqproceso. Por el contrario, esta variante está limitada por la longitud máxima permitida de la línea de comando, que suele ser mucho menor. Lo bueno es que este script más reciente se encargará de valores que contengan cualquier carácter, incluidas las nuevas líneas.

información relacionada