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. printf
es 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 while
un 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 jq
para 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 $line
en la jq
expresión. Se divide en el carácter de tabulación y la clave y el valor se agregan al objeto que reduce
está 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 jq
expresió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 jq
línea de comando muy larga que asocie cada clave con su valor usando --arg
y luego solicitar jq
que se generen estos (que se encontrarán en la $ARGS.named
estructura 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 jq
proceso. 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.