Tracce
/
jq
jq
/
Programma
/
Le basi
Le

Le basi in jq

1 esercizio

Informazioni su Le basi

jq funziona facendo passare i dati JSON di input attraverso una singola espressione (scritta come una pipeline di filtri) per ottenere i dati trasformati desiderati.

Una rapida introduzione alle opzioni della riga di comando di jq

Il linguaggio jq è implementato dal programma jq. Questo programma offre diverse comode opzioni della riga di comando per controllare come viene consumato l'input e come viene presentato l'output.

Negli esempi che seguono incontrerai:

  • -n o --null-input

    Normalmente al programma jq viene dato un file da leggere, oppure gli si inviano dati in input. L'opzione --null-input ti permette di generare dati JSON senza alcun input.

  • -c o --compact-output

    Per impostazione predefinita, jq formatta l'output in modo leggibile. È estremamente utile per gli esseri umani vedere i dati quando sono ben formattati. Per le macchine, però, non è necessario: l'opzione --compact-output rimuove gli spazi bianchi di formattazione per ridurre al minimo la dimensione del JSON risultante.

  • -f filename o --from-file filename

    Legge il programma jq da filename invece di fornirlo sulla riga di comando. Sia sed che awk usano l'opzione -f per lo stesso scopo. Lo vedrai usato negli script di test degli esercizi di pratica.

Vedi il manuale per i dettagli su tutte le opzioni.

Filtri e pipe

I filtri sono anche noti come espressioni.

Un filtro prende un input e produce un output. Come quando lavori in una shell unix, puoi unire i filtri con una pipe | per collegare l'output di un filtro all'input di un altro.

Il filtro identità: .

Questo è il filtro più semplice. Si limita a passare il suo input al suo output. Per esempio, jq formatta l'output in modo leggibile per impostazione predefinita, quindi passare del JSON al filtro . ti offre gratuitamente un output ben formattato!

$ echo '[1, 2, 3]' | jq '.'
[
  1,
  2,
  3
]

Gli array

Questa sarà una rapida introduzione a lavorare con gli array. Tratteremo questo argomento in modo più approfondito più avanti.

Gli elementi di un array si accedono con le parentesi quadre e hanno indici che partono da zero.

$ echo '[10, 20, 30]' | jq '.[1]'
20

Un filtro può costruire un array racchiudendo un'espressione tra [ e ]

  • con un elenco noto di elementi:

    jq -n '[1, 2, 3]'
    
  • per raccogliere un flusso di elementi: per esempio, range è una funzione che produce un flusso di numeri

    $ jq -n 'range(10; 70; 15)'
    10
    25
    40
    55
    

    Usare [] raccoglie i risultati dell'espressione in un array

    $ jq -c -n '[range(10; 70; 15)]'
    [10,25,40,55]
    

La virgola è un operatore

La virgola non è solo una sintassi che separa gli elementi di un array. La virgola è un operatore che unisce i flussi.

Per esempio, [1, 2, 3] è un filtro che usa il costruttore di array [] per raccogliere il risultato dell'unione delle tre espressioni 1, 2 e 3.

Hai notato i punti e virgola in range(10; 70; 15) più sopra? Dato che le virgole hanno uno scopo specifico nel linguaggio jq, le funzioni che accettano più argomenti usano i punti e virgola per separare gli argomenti.

Gli oggetti

Una rapida introduzione agli oggetti.

Come in molti linguaggi di programmazione, usa i punti per accedere alle proprietà di un oggetto

$ echo '{"foo": {"bar": "qux"}}' | jq '.foo.bar'
"qux"

Le parentesi quadre si possono usare anche per gli oggetti, ma in quel caso servono le virgolette per le stringhe letterali. Questo è un metodo per lavorare con chiavi che contengono spazi.

$ echo '{"foo bar": "qux"}' | jq '.["foo bar"]'
"qux"

Puoi costruire un oggetto con {} e coppie key: value. Le virgolette non sono necessarie attorno alle chiavi che sono stringhe «semplici».

jq -n '{question: (6 * 9), answer: 42}'

produce

{
  "question": 54,
  "answer": 42
}

Per trattare la chiave come un'espressione, devi racchiuderla tra parentesi (anche il codice seguente produce lo stesso output di sopra).

echo '[["question", "answer"], [54, 42]]' \
| jq '{(.[0][0]): .[1][0], (.[0][1]): .[1][1]}'
Note

È molto comune voler estrarre un sottoinsieme di chiavi da un oggetto grande. Per esempio, per estrarre id e name da

{
    "id": 101,
    "name": "alpha widget",
    "specifications": {...}
}

Potremmo scrivere

{id: .id, name: .name}

Ma è così comune che esiste una sintassi abbreviata:

{id, name}

Le pipeline

Per esempio, dato un file.json che contiene

{
  "key1": "value1",
  "key2": [5, 15, 25]
}

Calcoliamo la lunghezza dell'array key2:

$ jq '.key2 | length' file.json
3

Stiamo usando una pipe per passare l'output dell'espressione .key2 come input a length, che come prevedibile produce il numero di elementi nell'array.

Caution

Questo è un aspetto di jq a cui bisogna abituarsi: la maggior parte delle funzioni (ma non tutte) si comporta come i filtri, dove passi i dati all'input del filtro, non come argomento.

I filtri possono ignorare il loro input

In questo esempio, i dati JSON di input vengono ignorati e non hanno alcun impatto sull'output:

$ echo '{"answer": 42}' | jq '6 * 9'
54

I filtri possono produrre flussi di dati

Un filtro può produrre più di un valore. Per esempio, il filtro .[] produce ogni elemento di un array come valore separato:

$ jq -n -c '[1, 2, 3]'
[1,2,3]

$ jq -n -c '[1, 2, 3] | .[]'
1
2
3

Collegare con una pipe un simile filtro a un altro eseguirà il secondo filtro per ogni valore:

$ jq -n -c '[1, 2, 3] | .[] | . * 2'
2
4
6

È come un'iterazione implicita. Una volta compresa questa tecnica, capirai che i filtri jq più potenti possono essere molto concisi.

Le parentesi

Le parentesi si usano per raggruppare sottoespressioni e imporre l'ordine delle operazioni, proprio come in altri linguaggi. In jq, la necessità di usarle può sembrare un po' sorprendente.

Per esempio, supponiamo di voler costruire un array con 2 elementi: la radice quadrata di 9 e e elevato alla potenza 1. Le due espressioni singole sono 9 | sqrt e 1 | exp. Ci aspettiamo che l'output sia l'array [3, 2.7...]

$ jq -n '[ 9|sqrt, 1|exp ]'
[
  20.085536923187668,
  2.718281828459045
]

Perché non abbiamo ottenuto ciò che ci aspettavamo? jq interpreta il tutto così:

[ ((9|sqrt), 1) | exp ]

jq costruisce un flusso di due elementi (3 e 1) che vengono dati entrambi a exp.

Dobbiamo assicurarci che exp riceva un solo numero come input. In altre parole, dobbiamo imporre che la pipe venga valutata prima della virgola.

$ jq -n '[ 9|sqrt, (1|exp) ]'
[
  3,
  2.718281828459045
]

I tipi

Dal manuale

jq supporta lo stesso insieme di tipi di dato di JSON: numeri, stringhe, booleani, array, oggetti (che nel gergo JSON sono hash con sole chiavi di tipo stringa) e "null".

Imparerai di più su questi argomenti negli esercizi successivi.

Gli spazi bianchi

Gli spazi bianchi non sono significativi in jq. Usa spazi, tabulazioni e a capo come preferisci per formattare il tuo codice. Non siamo a conoscenza di guide di stile per jq.

Valori immutabili

I valori in jq sono immutabili. I filtri che modificano un valore producono un nuovo valore. Questo implica che jq non ha variabili globali: dovrai abituarti a passare lo stato da un filtro all'altro.

«Veridicità»

I valori false e null sono considerati falsi. Qualsiasi altro valore (compreso il numero zero e la stringa, l'array o l'oggetto vuoti) è vero.

Funzioni e operatori

Senza entrare troppo nei dettagli (le funzioni saranno l'argomento di un altro esercizio), ecco alcune utili funzioni integrate:

  • length

    Dato un array come input, produce il numero di elementi dell'array.

    $ jq -n '[10, 20, 30, 40] | length'
    4
    
  • +

    Questo operatore fa cose diverse a seconda del tipo dei suoi operandi: somma i numeri, concatena le stringhe, accoda gli array, unisce gli oggetti.

    $ jq -c -n '
        3 + 4,
        "foo" + "bar",
        ["a", "b"] + ["c"],
        {"m": 10} + {"n": 20}
    '
    7
    "foobar"
    ["a","b","c"]
    {"m":10,"n":20}
    

    add è una funzione che prende un array e restituisce un elemento con tutti gli elementi sommati insieme secondo le regole di +. [1, 2, 3] | add produce 6.

  • map

    Dato un array come input e un filtro come argomento, produce un array in cui il filtro è applicato a ogni elemento

    $ jq -c -n '[10, 20, 30, 40] | map(. / 5)'
    [2,4,6,8]
    
  • select

    Dato un certo input e un filtro come argomento:

    • se il filtro applicato all'argomento dà un valore vero, produce l'input invariato
    • altrimenti, non produce nulla (non il valore null, davvero nessun output)

    Per esempio, dati alcuni numeri, seleziona quelli divisibili per 3

    $ jq -n 'range(10) | select(. % 3 == 0)'
    0
    3
    6
    9
    

    Ricorda che range produce un flusso di numeri. select verrà chiamato una volta per ogni numero. Vengono prodotti solo i numeri che «passano» l'espressione.

    Spesso hai bisogno di selezionare elementi di un array. Ci sono un paio di modi per farlo.

    Con l'input ["Anne", "Bob", "Cathy", "Dave"], seleziona i nomi che hanno lunghezza 4.

    • usa map e select insieme

      map(select(length == 4))
      
    • scomponi l'array nei suoi elementi, applica select su quel flusso e raccogli i risultati

      [ .[] | select(length == 4) ]
      

I commenti

I commenti iniziano con il carattere # e proseguono fino alla fine della riga.

Modifica tramite GitHub Il collegamento si apre in una nuova finestra o scheda

Impara Le basi