Korrekte JSON-Konstruktion

Korrekte JSON-Konstruktion

Ich generiere eine JSON-Datei mit einem Shell-Skript, finde aber keine Lösung, um das letzte Komma direkt vor der Endung „}“ automatisch zu entfernen.

Hier ist mein Code:

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

Die Datei endet folgendermaßen:

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

Die Datei sollte folgendermaßen enden:

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

Wie kann ich das schaffen?

Der Code wurde bearbeitet, um hier besser lesbar zu sein.

Antwort1

Sie sollten das erste Element drucken und den Cursor anhalten lassen. Dann können Sie mit der Arbeit an der Schleife beginnen und den Cursor durch Einfügen von Kommas und Zeilenumbrüchen weiterbewegen. printfist in dieser Hinsicht hilfreicher als Echo. Um übermäßige Wiederholungen zu vermeiden und den Code zu verkürzen, verwenden Sie Funktionen.

Da ich nicht über die Dateien verfüge, die Sie mit awk analysieren, kann ich nicht mit Ihrem Skript arbeiten, aber das folgende Beispiel veranschaulicht, was ich zu vermitteln versuche.

#!/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"  "}"

Beispiellauf

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

Der obige Ansatz ist normalerweise das, was ich „Komma dahinter hinzufügen“ nenne. Ein anderer Ansatz wäre „Komma davor hinzufügen“, aber anstelle einer For-Schleife verwenden Sie whileeine Schleife mit Zähler, um das Verhalten einer For-Schleife im C-Stil zu simulieren. Wenn Sie das letzte Element erreichen, geben Sie kein Komma aus. Grundsätzlich

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

Antwort2

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

Antwort3

Erstellen Sie aus einem Datenstrom tabulatorgetrennter Schlüssel- und Wertepaare ein einzelnes JSON-Objekt, das diese Schlüssel und Werte enthält. (Wenn die Daten tatsächlich Tabulatoren enthalten, verwenden Sie einfach ein Zeichen, das nicht Teil der Daten ist.)

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

Dies verwendet jqdas Lesen eines Zeilenstroms, jede mit einem Schlüssel und einem Wert, die durch ein Tabulatorzeichen getrennt sind. Jede Zeile wird gelesen und wie $lineim jqAusdruck verwendet. Sie wird am Tabulatorzeichen aufgeteilt, und der Schlüssel und der Wert werden dem Objekt hinzugefügt, das die reduceAnweisung akkumuliert. Das Objekt wächst, wenn neue Schlüssel und Werte hinzugefügt werden, und wenn der Eingabestrom endet, wird das fertige Objekt ausgegeben.

Dies ist eine schön gedruckte Version des jqAusdrucks:

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

Ein funktionierendes Beispielskript:

#!/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})'

Ausgabe:

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

Wenn es nicht zu viele Schlüssel und Werte gibt, können wir dies vereinfachen und nur eine sehr lange jqBefehlszeile erstellen, die jeden Schlüssel mithilfe von seinem Wert verknüpft --arg, und dann die Ausgabe dieser Werte anfordern (die in der internen Struktur jqzu finden sind ):$ARGS.named

#!/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'

In diesem Fall ist die Ausgabe identisch mit der Ausgabe des vorherigen Skripts. Der Unterschied besteht darin, dass die Anzahl der Schlüssel und Werte, die das vorherige Skript verarbeiten kann, durch den für den jqProzess verfügbaren Speicher begrenzt ist. Im Gegensatz dazu ist diese Variante durch die maximal zulässige Länge der Befehlszeile begrenzt, die normalerweise viel kleiner ist. Der Vorteil ist, dass dieses neueste Skript mit Werten zurechtkommt, die beliebige Zeichen enthalten, einschließlich Zeilenumbrüchen.

verwandte Informationen