Το jq λειτουργεί περνώντας τα εισερχόμενα δεδομένα JSON μέσα από μία ενιαία έκφραση (γραμμένη ως σωλήνωση φίλτρων) για να πετύχεις τα επιθυμητά μετασχηματισμένα δεδομένα.
jq
Η γλώσσα jq υλοποιείται από το πρόγραμμα jq.
Αυτό το πρόγραμμα παρέχει αρκετές βολικές επιλογές γραμμής εντολών για να ελέγχεις πώς καταναλώνεται η είσοδος και πώς παρουσιάζεται η έξοδος.
Στα παρακάτω παραδείγματα θα συναντήσεις:
-n ή --null-input
Κανονικά στο πρόγραμμα jq δίνεται ένα αρχείο για ανάγνωση, ή στέλνεις δεδομένα στην είσοδό του.
Η επιλογή --null-input σου επιτρέπει να παράγεις δεδομένα JSON χωρίς καμία είσοδο.
-c ή --compact-output
Το jq μορφοποιεί όμορφα την έξοδό του από προεπιλογή.
Είναι εξαιρετικά χρήσιμο για τους ανθρώπους να βλέπουν τα δεδομένα όταν είναι όμορφα μορφοποιημένα.
Ωστόσο, αυτό δεν είναι απαραίτητο για τις μηχανές: η επιλογή --compact-output αφαιρεί τα κενά μορφοποίησης για να ελαχιστοποιήσει το μέγεθος του JSON που προκύπτει.
-f filename ή --from-file filename
Διάβασε το πρόγραμμα jq από το filename αντί να το δίνεις στη γραμμή εντολών.
Τόσο το sed όσο και το awk χρησιμοποιούν την επιλογή -f για τον ίδιο σκοπό.
Θα το δεις αυτό να χρησιμοποιείται στα σενάρια δοκιμών για τις ασκήσεις πρακτικής.
Δες το εγχειρίδιο για λεπτομέρειες σχετικά με όλες τις επιλογές.
Τα φίλτρα είναι επίσης γνωστά ως εκφράσεις.
Ένα φίλτρο δέχεται μια είσοδο και παράγει μια έξοδο.
Όπως ακριβώς δουλεύεις σε ένα κέλυφος unix, μπορείς να ενώσεις φίλτρα με έναν σωλήνα | για να συνδέσεις την έξοδο του ενός φίλτρου με την είσοδο του άλλου.
.
Αυτό είναι το πιο απλό φίλτρο.
Περνά απλώς την είσοδό του στην έξοδό του.
Για παράδειγμα, το jq μορφοποιεί όμορφα από προεπιλογή, οπότε περνώντας JSON στο φίλτρο . παίρνεις δωρεάν όμορφα μορφοποιημένη έξοδο!
$ echo '[1, 2, 3]' | jq '.'
[
1,
2,
3
]
Αυτή θα είναι μια γρήγορη εισαγωγή στην εργασία με πίνακες. Θα καλύψουμε αυτό το θέμα με περισσότερες λεπτομέρειες αργότερα.
Τα στοιχεία ενός πίνακα προσπελαύνονται με αγκύλες και η αρίθμησή τους ξεκινά από το μηδέν.
$ echo '[10, 20, 30]' | jq '.[1]'
20
Ένα φίλτρο μπορεί να φτιάξει έναν πίνακα τυλίγοντας μια έκφραση σε [ και ]
με μια γνωστή λίστα στοιχείων:
jq -n '[1, 2, 3]'
για να συλλέξεις μια ροή στοιχείων: για παράδειγμα,
η range είναι μια συνάρτηση που παράγει μια ροή αριθμών
$ jq -n 'range(10; 70; 15)'
10
25
40
55
Η χρήση του [] συλλέγει τα αποτελέσματα της έκφρασης σε έναν πίνακα
$ jq -c -n '[range(10; 70; 15)]'
[10,25,40,55]
Το κόμμα δεν είναι απλώς σύνταξη που χωρίζει τα στοιχεία ενός πίνακα. Το κόμμα είναι ένας τελεστής που ενώνει ροές.
Για παράδειγμα, το [1, 2, 3] είναι ένα φίλτρο που χρησιμοποιεί τον κατασκευαστή πίνακα [] για να συλλέξει το αποτέλεσμα της ένωσης των τριών εκφράσεων 1, 2 και 3.
Πρόσεξες τις άνω τελείες στο range(10; 70; 15) παραπάνω;
Επειδή τα κόμματα έχουν συγκεκριμένο σκοπό στη γλώσσα jq, οι συναρτήσεις που δέχονται πολλαπλά ορίσματα χρησιμοποιούν άνω τελείες για να χωρίσουν τα ορίσματα.
Μια γρήγορη εισαγωγή στα αντικείμενα.
Όπως σε πολλές γλώσσες προγραμματισμού, χρησιμοποίησε τελείες για να προσπελάσεις τις ιδιότητες ενός αντικειμένου
$ echo '{"foo": {"bar": "qux"}}' | jq '.foo.bar'
"qux"
Οι αγκύλες μπορούν να χρησιμοποιηθούν και για αντικείμενα, αλλά τότε χρειάζονται εισαγωγικά για τα κυριολεκτικά συμβολοσειρών. Αυτός είναι ένας τρόπος να δουλέψεις με κλειδιά που περιέχουν κενά.
$ echo '{"foo bar": "qux"}' | jq '.["foo bar"]'
"qux"
Μπορείς να κατασκευάσεις ένα αντικείμενο με {} και ζεύγη key: value.
Τα εισαγωγικά δεν είναι απαραίτητα γύρω από κλειδιά που είναι "απλές" συμβολοσειρές.
jq -n '{question: (6 * 9), answer: 42}'
παράγει
{
"question": 54,
"answer": 42
}
Για να αντιμετωπίσεις το κλειδί ως έκφραση, πρέπει να το τυλίξεις σε παρενθέσεις (το παρακάτω δίνει επίσης το ίδιο αποτέλεσμα όπως παραπάνω).
echo '[["question", "answer"], [54, 42]]' \
| jq '{(.[0][0]): .[1][0], (.[0][1]): .[1][1]}'
Είναι αρκετά συνηθισμένο να θέλεις να εξαγάγεις ένα υποσύνολο κλειδιών από ένα μεγάλο αντικείμενο.
Για παράδειγμα, για να εξαγάγεις τα id και name από
{
"id": 101,
"name": "alpha widget",
"specifications": {...}
}
Θα μπορούσαμε να γράψουμε
{id: .id, name: .name}
Αλλά αυτό είναι τόσο συνηθισμένο που υπάρχει συντομευμένη σύνταξη γι' αυτό:
{id, name}
Για παράδειγμα, δεδομένου του file.json που περιέχει
{
"key1": "value1",
"key2": [5, 15, 25]
}
Ας υπολογίσουμε το μήκος του πίνακα key2:
$ jq '.key2 | length' file.json
3
Διοχετεύουμε την έξοδο της έκφρασης .key2 ως είσοδο στη length, η οποία, όπως είναι αναμενόμενο, παράγει τον αριθμό των στοιχείων του πίνακα.
Αυτή είναι μια πτυχή του jq που θέλει λίγο χρόνο για να τη συνηθίσεις. Οι περισσότερες (αλλά όχι όλες) οι συναρτήσεις συμπεριφέρονται σαν φίλτρα, όπου περνάς δεδομένα στην είσοδο του φίλτρου, όχι ως όρισμα.
Σε αυτό το παράδειγμα, τα δεδομένα JSON εισόδου αγνοούνται και δεν έχουν καμία επίδραση στην έξοδο:
$ echo '{"answer": 42}' | jq '6 * 9'
54
Ένα φίλτρο μπορεί να παράγει περισσότερες από μία τιμές.
Για παράδειγμα, το φίλτρο .[] παράγει κάθε στοιχείο ενός πίνακα ως ξεχωριστή τιμή:
$ jq -n -c '[1, 2, 3]'
[1,2,3]
$ jq -n -c '[1, 2, 3] | .[]'
1
2
3
Διοχετεύοντας ένα τέτοιο φίλτρο σε ένα άλλο, θα εκτελεστεί το 2ο φίλτρο για κάθε τιμή:
$ jq -n -c '[1, 2, 3] | .[] | . * 2'
2
4
6
Αυτό μοιάζει με έμμεση επανάληψη.
Μόλις καταλάβεις αυτήν την τεχνική, θα συνειδητοποιήσεις ότι ισχυρότατα φίλτρα jq μπορούν να είναι πολύ σύντομα.
Οι παρενθέσεις χρησιμοποιούνται για να ομαδοποιήσουν υποεκφράσεις μαζί, ώστε να επιβληθεί η σειρά των πράξεων, όπως ακριβώς και σε άλλες γλώσσες.
Στη jq, η ανάγκη γι' αυτές μπορεί να φαίνεται κάπως αναπάντεχη.
Για παράδειγμα, ας πούμε ότι θέλουμε να κατασκευάσουμε έναν πίνακα με 2 στοιχεία: την τετραγωνική ρίζα του 9 και το e υψωμένο στη δύναμη 1.
Οι δύο μεμονωμένες εκφράσεις είναι 9 | sqrt και 1 | exp.
Περιμένουμε η έξοδος να είναι ο πίνακας [3, 2.7...]
$ jq -n '[ 9|sqrt, 1|exp ]'
[
20.085536923187668,
2.718281828459045
]
Γιατί δεν πήραμε αυτό που περιμέναμε; Το jq το ερμηνεύει ως εξής:
[ ((9|sqrt), 1) | exp ]
Το jq φτιάχνει μια ροή δύο στοιχείων (3 και 1), τα οποία δίνονται το καθένα στο exp.
Πρέπει να διασφαλίσουμε ότι το exp παίρνει μόνο έναν αριθμό ως είσοδο.
Με άλλα λόγια, πρέπει να επιβάλουμε ο σωλήνας να αξιολογείται πριν από το κόμμα.
$ jq -n '[ 9|sqrt, (1|exp) ]'
[
3,
2.718281828459045
]
Από το εγχειρίδιο
Το jq υποστηρίζει το ίδιο σύνολο τύπων δεδομένων με το JSON: αριθμούς, συμβολοσειρές, Boolean (λογική τιμή), πίνακες, αντικείμενα (τα οποία στην ορολογία του JSON είναι hashes με κλειδιά μόνο συμβολοσειρών) και "null".
Θα μάθεις περισσότερα γι' αυτά σε επόμενες ασκήσεις.
Τα κενά διαστήματα δεν είναι σημαντικά στη jq.
Χρησιμοποίησε κενά, στηλοθέτες και αλλαγές γραμμής όπως θέλεις για να μορφοποιήσεις τον κώδικά σου.
Δεν γνωρίζουμε να υπάρχει κανένας οδηγός στυλ για τη jq.
Οι τιμές στη jq είναι αμετάβλητες.
Τα φίλτρα που τροποποιούν μια τιμή θα παράγουν μια νέα τιμή.
Αυτό σημαίνει ότι η jq δεν έχει καθολικές μεταβλητές. Θα πρέπει να συνηθίσεις να περνάς κατάσταση από το ένα φίλτρο στο άλλο.
Οι τιμές false και null θεωρούνται ψευδείς. Οποιαδήποτε άλλη τιμή
(συμπεριλαμβανομένου του αριθμού μηδέν και της κενής συμβολοσειράς/πίνακα/αντικειμένου) είναι αληθής.
Χωρίς να μπούμε σε μεγάλο βάθος (οι συναρτήσεις θα είναι θέμα άλλης άσκησης), ορίστε μερικές χρήσιμες ενσωματωμένες συναρτήσεις:
Δεδομένου ενός πίνακα ως είσοδο, παράγει τον αριθμό των στοιχείων του πίνακα.
$ jq -n '[10, 20, 30, 40] | length'
4
Αυτός ο τελεστής κάνει διαφορετικά πράγματα ανάλογα με τον τύπο των τελεστέων του: προσθέτει αριθμούς, συνενώνει συμβολοσειρές, προσθέτει στοιχεία σε πίνακες, συγχωνεύει αντικείμενα.
$ jq -c -n '
3 + 4,
"foo" + "bar",
["a", "b"] + ["c"],
{"m": 10} + {"n": 20}
'
7
"foobar"
["a","b","c"]
{"m":10,"n":20}
Η add είναι μια συνάρτηση που παίρνει έναν πίνακα και επιστρέφει ένα στοιχείο με όλα τα στοιχεία αθροισμένα μεταξύ τους με τους κανόνες του +.
Το [1, 2, 3] | add παράγει 6.
Δεδομένου ενός πίνακα ως είσοδο και ενός φίλτρου ως όρισμα, παράγει έναν πίνακα όπου το φίλτρο εφαρμόζεται σε κάθε στοιχείο
$ jq -c -n '[10, 20, 30, 40] | map(. / 5)'
[2,4,6,8]
Δεδομένης κάποιας εισόδου και ενός φίλτρου ως όρισμα:
null, αλλά πραγματικά καμία έξοδο)Για παράδειγμα, δεδομένων κάποιων αριθμών, επίλεξε αυτούς που διαιρούνται με το 3
$ jq -n 'range(10) | select(. % 3 == 0)'
0
3
6
9
Θυμήσου ότι η range παράγει μια ροή αριθμών.
Η select θα καλείται μία φορά για κάθε αριθμό.
Μόνο οι αριθμοί που "περνούν" την έκφραση παράγονται.
Συχνά χρειάζεται να επιλέξεις στοιχεία ενός πίνακα. Υπάρχουν μερικοί τρόποι να το κάνεις αυτό.
Με είσοδο ["Anne", "Bob", "Cathy", "Dave"], επίλεξε τα ονόματα που έχουν μήκος 4.
χρησιμοποίησε τις map και select μαζί
map(select(length == 4))
σπάσε τον πίνακα στα στοιχεία του, εφάρμοσε select σε αυτή τη ροή και συγκέντρωσε τα αποτελέσματα
[ .[] | select(length == 4) ]
Τα σχόλια ξεκινούν με τον χαρακτήρα # και συνεχίζουν μέχρι το τέλος της γραμμής.