Ich habe bereits über Shell-Skripte für JSON mit Node.js gebloggt. In diesem Beitrag zeige ich Ihnen, wie Sie dasselbe mit jsawk erreichen können. Wie das so ist, habe ich von jsawk erst erfahren, nachdem ich meinen Beitrag über Shell-Scripting mit Node geschrieben hatte. Es ist gut, beides zu wissen :).
Jsawk soll, wie der Name schon sagt, für json das sein, was awk für strukturierten Klartext ist. Es ist ziemlich nützlich, da es im Vergleich zu den Node.js-Skripten, die ich zuvor gezeigt habe, eine Menge an Setup/Boilerplate spart.
Ein einfaches Beispiel
Nehmen wir eine csv-Datei mit Aktienkursen:
[sourcecode language="text"]
#Symbol,Price,Cap
GOOG,569.31,184.58B
AAPL,574.70,537.60B
[/sourcecode]
Wenn wir nur die Symbole aus dieser Datei mit awk extrahieren wollen, eines pro Zeile, können wir die Datei mit awk ausführen:
[sourcecode language="bash"]awk -F , '/^[^#]/{ print $1 }'[/sourcecode]
To get the sum of all stock prices in the portfolio (which is silly), we run the file through in a slightly different way:
[sourcecode language="bash"]awk -F , '/^[^#]/{ sum += $2 } END{ print sum }'[/sourcecode]
Für diejenigen, die mit awk nicht vertraut sind: Das Argument -F , gibt an, dass wir das Komma als Feldtrenner verwenden. Die Regex
Wenn wir die gleichen Informationen in einem JSON-Dokument hätten, würde es wahrscheinlich so aussehen:
[sourcecode language="javascript"]
[
{
"Symbol":"GOOG",
"Price":"569.31",
"Cap":"184.58B"
},
{
"Symbol":"AAPL",
"Price":"574.70",
"Cap":"537.60B"
}
]
[/sourcecode]
Bei dieser Art von Eingabe ist die Ähnlichkeit zwischen awk und jsawk sehr groß. Wir werden weiter unten sehen, dass es ein wenig zusammenbricht, wenn die Wurzel des JSON ein Objekt statt eines Arrays ist, aber lassen Sie uns zunächst auf die einfachen Dinge konzentrieren.
Um die Symbolnamen mit jsawk zu extrahieren, können wir die json-Daten durch ein sehr einfaches
[sourcecode language="bash"]jsawk 'return this.Symbol'[/sourcecode] and get the names in a JSON array: ["AAPL","GOOG"]. This script is analogous to applying ECMAscript 5's map function: it returns a new array where each element is the result of applying the given function to the element in the original array. The body of this function is the only parameter to jsawk, we don't need any boilerplate.
If we want to get the names unquoted and one per line, like with the awk script we've seen earlier, we can run [sourcecode language="bash"]jsawk -n 'out(this.Symbol)'[/sourcecode]
Hier zeigt das Flag -n an, dass wir nicht an dem umgewandelten JSON interessiert sind. Die Funktion out(arg) schreibt einfach an den Standardausgang.
Beide Skripte sind leichter zu lesen als ihre awk-Gegenstücke, vor allem weil ich im CSV-Beispiel eine Kopfzeile eingefügt habe. Bei einer CSV-Datei ohne Kopfzeile sind die awk- und jsawk-Skripte gleichwertig. Das Skript zur Rückgabe der Summe der Aktienkurse ist leider komplizierter als sein awk-Äquivalent: [sourcecode language="bash"]jsawk -n -v sum -b 'sum = 0.0' 'sum = sum + parseFloat(this.Price)' -a 'out(sum)'[/sourcecode]. Lassen Sie uns das aufschlüsseln:
- Wir haben bereits den Parameter -n gesehen, der jsawk anweist, die json-Umwandlung zu ignorieren.
- Das Argument -v sum definiert eine globale Variable namens sum. Sie können optional einen Anfangswert übergeben, z.B. -v sum=0, wodurch die Variable mit der Zeichenkette "0" initialisiert wird.
- Das Argument -b <script> definiert ein Skript, das einmal ausgeführt wird, bevor die Eingabe verarbeitet wird.
- Der Operator -a <script> definiert ein Skript, das nach der Verarbeitung der Eingaben einmal ausgeführt wird.
Das Vorher-Skript initialisiert die Variable Summe als Zahl. Wenn wir das nicht tun, führt der Plus-Operator im Hauptskript eher eine String-Verkettung als eine numerische Addition durch. Das Hauptskript addiert jeden Aktienkurs zur Summenvariablen. Das After-Skript gibt das Ergebnis aus.
Was ist dann mit Gegenständen?
Ich habe bereits erwähnt, dass die Analogie von jsawk zu awk leidet, wenn das Wurzelelement des JSON-Dokuments ein Objekt ist. Es ist vielleicht unfair zu sagen, dass eine Eingabe, die nur aus einem Datensatz besteht, einfach weniger interessant ist als eine Eingabe, die aus mehreren Datensätzen besteht, sowohl bei awk als auch bei jsawk.
Schauen wir uns noch einmal die Beispieldaten aus meinem Beitrag über die Skripterstellung mit Node.js an. Sie werden sich erinnern, dass wir diese CouchDB-Ansicht umwandeln mussten:
[sourcecode language="javascript"]
{"total_rows":22,"offset":0,"rows":[
{"id":"123456","key":"123456","value":{"rev":"1-acf7f39495a2cd4465be504cd435629e"}},
{"id":"123457","key":"123457","value":{"rev":"1-b67df18954264dbb65bf341294e572a5"}}
...20 more...
]}
[/sourcecode]
...in diese Bulk-Api-Nachricht:
[sourcecode language="javascript"]
{ "docs":[
{"_id":"123456","_rev":"1-acf6f39495a2cd4465be504cd435629e","_deleted":true}
{"_id":"123457","_rev":"1-b67df18954264dbb65be341294e572a5","_deleted":true}
...20 more...
]}
[/sourcecode]
Das sieht einfach genug aus, aber wenn Sie diese Eingabe durch jsawk leiten, werden Sie feststellen, dass das Skript genau einmal für das gesamte View-Objekt aufgerufen wird. Eine einfache Möglichkeit, dies zu umgehen, besteht darin, jsawk zweimal in Ihre Shell-Pipeline aufzunehmen: [sourcecode language="bash"]jsawk 'return this.rows' | jsawk -n 'out("record")'[/sourcecode] Vielleicht gibt es einen besseren Weg, und ich vermute, dass die Funktion put(record) für diesen Zweck gedacht ist, aber ich konnte nicht herausfinden, wie sie funktioniert.
Mithilfe der doppelten jsawk-Technik kann ich die CouchDB-Ansicht wie folgt in die Bulk-Api-Nachricht umwandeln:
[sourcecode language="bash"]jsawk 'return this.rows' |
jsawk 'return {_id:this.id,_rev:this.value.rev,_deleted:true}'
-a 'return {docs:this}'[/sourcecode]
Das Hauptskript des zweiten jsawk-Befehls konstruiert ein Objektliteral für jeden Datensatz in der Eingabe, wobei die ID und die Revision kopiert und eine hartkodierte Löschmarkierung eingefügt wird, um die Sicherheit zu erhöhen. Das Nachskript wickelt das generierte JSON-Array in ein Objektliteral ein.
Das funktioniert gut und es ist viel weniger Code zu pflegen.
Verfasst von

Barend Garvelink
Contact



