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. printf
ist 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 while
eine 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 jq
das Lesen eines Zeilenstroms, jede mit einem Schlüssel und einem Wert, die durch ein Tabulatorzeichen getrennt sind. Jede Zeile wird gelesen und wie $line
im jq
Ausdruck verwendet. Sie wird am Tabulatorzeichen aufgeteilt, und der Schlüssel und der Wert werden dem Objekt hinzugefügt, das die reduce
Anweisung 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 jq
Ausdrucks:
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 jq
Befehlszeile erstellen, die jeden Schlüssel mithilfe von seinem Wert verknüpft --arg
, und dann die Ausgabe dieser Werte anfordern (die in der internen Struktur jq
zu 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 jq
Prozess 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.