Filtrowanie i wybieranie JSON za pomocą potoków jq
Nawiguj po zagnieżdżonych obiektach i tablicach za pomocą selektorów jq, potoków oraz filtra select.
Filtrowanie i wybieranie JSON za pomocą potoków jq to bezpłatna lekcja DevOps Bootcamp na CoddyKit. To lekcja 1 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej DevOps Bootcamp, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs DevOps Bootcamp zawiera 4 lekcji w sumie.
Czym jest jq i dlaczego warto go używać?
jq to lekkie, zaawansowane narzędzie wiersza poleceń do analizowania, filtrowania i przekształcania danych JSON. Jest odpowiednikiem sed dla JSON — przekazuje się do niego JSON potokiem i otrzymuje z powrotem uporządkowane dane wyjściowe.
- Jest preinstalowane w większości dystrybucji Linuksa lub dostępne za pomocą
apt install jq/brew install jq - Bezproblemowo działa w potokach powłoki wraz z
curl,cati innymi narzędziami - Obsługuje filtrowanie, mapowanie, redukcję i konwersję formatów
Podstawowe wywołanie ma postać: jq '<filter>' file.json albo jest używane w potoku jako cat file.json | jq '<filter>'. Filtr . (kropka) jest tożsamościowy — wyświetla cały dokument w czytelnie sformatowanej postaci.
# Pretty-print a JSON file
jq '.' data.json
# Or pipe from curl
curl -s https://api.github.com/users/torvalds | jq '.'Wybieranie pól obiektu za pomocą notacji kropkowej
Aby uzyskać dostęp do pola w obiekcie JSON, należy użyć notacji kropkowej: .fieldName. Selektory można łączyć, aby poruszać się po zagnieżdżonych obiektach.
.name— pole najwyższego poziomu.address.city— pole zagnieżdżone."field-with-dash"— pola ze znakami specjalnymi wymagają cudzysłowów
Jeśli pole nie istnieje, jq zwraca null zamiast zgłaszać błąd. Dzięki temu można bezpiecznie używać go w skryptach bez dodatkowego sprawdzania wartości null dla pól opcjonalnych.
# Given: {"name":"Alice","address":{"city":"Berlin","zip":"10115"}}
echo '{"name":"Alice","address":{"city":"Berlin","zip":"10115"}}' | jq '.name'
# Output: "Alice"
echo '{"name":"Alice","address":{"city":"Berlin","zip":"10115"}}' | jq '.address.city'
# Output: "Berlin"Dostęp do elementów tablicy i iterowanie
Dostęp do tablic JSON uzyskuje się za pomocą notacji nawiasowej. jq używa indeksowania od zera.
.items[0]— pierwszy element.items[-1]— ostatni element.items[1:3]— wycinek (od indeksu 1 do indeksu 3, bez indeksu 3).items[]— rozwija tablicę: wyświetla każdy element jako osobną wartość (jest to iterator)
Iterator [] ma podstawowe znaczenie w potokach jq — umożliwia niezależne zastosowanie kolejnych filtrów do każdego elementu.
# Given an array of users
echo '[{"name":"Alice"},{"name":"Bob"},{"name":"Carol"}]' | jq '.[0]'
# Output: {"name":"Alice"}
# Iterate all elements and extract .name from each
echo '[{"name":"Alice"},{"name":"Bob"},{"name":"Carol"}]' | jq '.[].name'
# Output:
# "Alice"
# "Bob"
# "Carol"Budowanie potoków jq za pomocą operatora potoku
Podobnie jak powłokowy potok |, jq ma własny wewnętrzny operator potoku. Przekazuje on dane wyjściowe jednego filtra jako dane wejściowe następnego.
jq '.users[] | .name'— iteruje po użytkownikach, a następnie pobiera nazwę każdego z nichjq '.data | .items[] | .id'— przechodzi do danych, rozwija tablicę items, a następnie pobiera identyfikator
Potoki wewnątrz wyrażenia jq pozwalają budować złożone przekształcenia krok po kroku. Każdy etap otrzymuje wszystko, co wygenerował poprzedni etap — w tym wiele wartości pochodzących z iteratora.
Najważniejsza zasada: gdy iterator generuje N wartości, każdy filtr następujący po nim jest uruchamiany N razy, po jednym razie dla każdej wartości.
# Nested pipeline: navigate -> iterate -> extract
echo '{"users":[{"name":"Alice","age":30},{"name":"Bob","age":25}]}' \
| jq '.users[] | .name'
# Output:
# "Alice"
# "Bob"
# Chain more stages
echo '{"users":[{"name":"Alice","age":30},{"name":"Bob","age":25}]}' \
| jq '.users[] | .age'
# Output:
# 30
# 25Filtrowanie za pomocą select()
Filtr select(condition) przepuszcza wartość tylko wtedy, gdy warunek jest prawdziwy; w przeciwnym razie nie generuje żadnych danych wyjściowych. Jest odpowiednikiem grep lub klauzuli WHERE w SQL.
select(.age > 18)— zachowuje obiekty, w których wiek jest większy niż 18select(.status == "active")— sprawdza równośćselect(.name | startswith("A"))— zagnieżdżony test ciągu znaków
Połącz select z iteratorem, aby filtrować tablice: .items[] | select(.active) wyświetla wyłącznie elementy, dla których .active ma wartość prawdziwą.
# Filter array elements by a condition
echo '[{"name":"Alice","age":30},{"name":"Bob","age":17},{"name":"Carol","age":25}]' \
| jq '.[] | select(.age >= 18) | .name'
# Output:
# "Alice"
# "Carol"
# Filter by string equality
echo '[{"name":"Alice","role":"admin"},{"name":"Bob","role":"user"}]' \
| jq '.[] | select(.role == "admin") | .name'
# Output: "Alice"Odtwarzanie obiektów i tablic za pomocą {} oraz []
jq umożliwia zmianę kształtu danych przez konstruowanie nowych obiektów za pomocą {} oraz nowych tablic za pomocą [].
{name: .name, city: .address.city}— wybiera pola i zmienia ich nazwy w nowym obiekcie[.items[] | .id]— zbiera iterowane wartości z powrotem do tablicy- Skrócona forma:
{name, age}jest równoważna{name: .name, age: .age}
Ujęcie potoku w [...] nazywa się konstrukcją tablicy i jest niezbędne, gdy dane wyjściowe mają być tablicą JSON, a nie strumieniem wartości.
# Reshape: keep only selected fields
echo '[{"id":1,"name":"Alice","password":"secret"},{"id":2,"name":"Bob","password":"secret"}]' \
| jq '[.[] | {id, name}]'
# Output:
# [
# {"id": 1, "name": "Alice"},
# {"id": 2, "name": "Bob"}
# ]
# Collect filtered names into an array
echo '[{"name":"Alice","active":true},{"name":"Bob","active":false}]' \
| jq '[.[] | select(.active) | .name]'
# Output: ["Alice"]Praca z zagnieżdżonymi tablicami i rekurencyjne przeszukiwanie
Rzeczywiste dane JSON często są głęboko zagnieżdżone. jq udostępnia dwa narzędzia do nawigowania po głębokiej strukturze:
.a.b.c— jawna ścieżka, gdy struktura jest znana.. | .fieldName?— rekurencyjne przeszukiwanie: przechodzi po każdym węźle drzewa i wyświetla wartości w węzłach, w których istnieje dany klucz
Operator ? (try) tłumi błędy, gdy pole nie istnieje w danym węźle. Ma to kluczowe znaczenie podczas rekurencyjnego przeszukiwania niejednorodnych drzew.
Z rekurencyjnego przeszukiwania dużych dokumentów należy korzystać oszczędnie — odwiedza ono każdy węzeł i może działać wolno. Jeśli struktura jest przewidywalna, należy preferować jawne ścieżki.
# Explicit deep path
echo '{"a":{"b":{"c":42}}}' | jq '.a.b.c'
# Output: 42
# Recursive descent: find all "id" values anywhere in the tree
echo '{"users":[{"id":1,"profile":{"id":99}},{"id":2}]}' \
| jq '.. | .id?'
# Output:
# 1
# 99
# 2Przykład praktyczny: analizowanie odpowiedzi API curl
Jednym z najczęstszych zastosowań jq jest analizowanie odpowiedzi REST API pobranych za pomocą curl. Połączenie curl -s (trybu cichego) z potokiem jq zapewnia przejrzyste wydobywanie danych, które można łatwo wykorzystać w skryptach.
- Wyodrębnienie pojedynczej wartości:
curl -s URL | jq '.field' - Budowanie tabeli podsumowania: iterowanie po tablicy i odtwarzanie obiektów zawierających tylko potrzebne pola
- Użycie
-r(surowe dane wyjściowe) do usunięcia otaczających cudzysłowów z wartości tekstowych — jest to niezbędne podczas przypisywania ich do zmiennych powłoki
Wskazówka: należy zawsze dodawać -r, gdy dane wyjściowe jq będą używane jako zmienna powłoki lub przesyłane do innego polecenia.
#!/usr/bin/env bash
# Fetch GitHub repo info and extract specific fields
REPO="torvalds/linux"
RESPONSE=$(curl -s "https://api.github.com/repos/${REPO}")
# Extract fields
STARS=$(echo "$RESPONSE" | jq -r '.stargazers_count')
LANG=$(echo "$RESPONSE" | jq -r '.language')
DESC=$(echo "$RESPONSE" | jq -r '.description')
echo "Stars : $STARS"
echo "Lang : $LANG"
echo "Desc : $DESC"Używanie map() i map_values()
jq udostępnia dwie wygodne funkcje wyższego rzędu do przekształcania kolekcji:
map(f)— stosuje filtrfdo każdego elementu tablicy i zwraca nową tablicę. Jest równoważne[.[] | f].map_values(f)— stosujefdo każdej wartości w obiekcie lub tablicy, zachowując klucze i indeksy.
Funkcje te są czytelniejsze niż ręczne opakowywanie potoków w [] i stanowią idiomatyczny styl jq w przypadku przekształceń, których wynikiem powinny pozostać tablice.
# map: extract a field from each element
echo '[{"name":"Alice","score":95},{"name":"Bob","score":80}]' \
| jq 'map(.name)'
# Output: ["Alice", "Bob"]
# map with select: filter + transform in one step
echo '[{"name":"Alice","score":95},{"name":"Bob","score":60}]' \
| jq 'map(select(.score >= 70) | .name)'
# Output: ["Alice"]
# map_values: multiply every value in an object by 2
echo '{"a":1,"b":2,"c":3}' | jq 'map_values(. * 2)'
# Output: {"a":2,"b":4,"c":6}Obsługa pól opcjonalnych i wartości domyślnych za pomocą //
Dane JSON ze źródeł zewnętrznych często są niespójne — pól może brakować albo mogą mieć wartość null. jq udostępnia operator alternatywy // (podwójny ukośnik), który pozwala podać wartość domyślną.
.nickname // "anonymous"— używa.nickname, jeśli nie ma ona wartości null ani false; w przeciwnym razie używa"anonymous".count // 0— wartość domyślna typu liczbowego- Można połączyć z
select:select((.status // "inactive") == "active")
Jest to znacznie krótsze niż odpowiednik powłoki ${VAR:-default} i dobrze sprawdza się wewnątrz dłuższych potoków.
# Provide defaults for missing/null fields
echo '[{"name":"Alice","role":"admin"},{"name":"Bob"}]' \
| jq '[.[] | {name, role: (.role // "user")}]'
# Output:
# [
# {"name": "Alice", "role": "admin"},
# {"name": "Bob", "role": "user"}
# ]
# Numeric default
echo '{"items":[1,2,3]}' | jq '.total // 0'
# Output: 0Praktyczny skrypt: analizator dzienników JSON
Rejestrowanie strukturalnych danych w formacie JSON jest standardem we współczesnych systemach. Oto realistyczny skrypt, który odczytuje plik dziennika zawierający po jednym obiekcie JSON w każdym wierszu, filtruje wpisy dotyczące błędów i formatuje czytelne dla człowieka podsumowanie.
Wykorzystane najważniejsze schematy:
-c(zwarty format danych wyjściowych) — jeden obiekt JSON w każdym wierszu, co jest przydatne podczas przekazywania danych do pętli powłoki--arg name value— wstrzykuje zmienną powłoki jako argument tekstowy jqselectdo filtrowania według poziomu dziennika-rdo generowania surowych danych tekstowych odpowiednich dlaecho
#!/usr/bin/env bash
# Parse newline-delimited JSON logs and report ERRORs
# Each log line: {"level":"ERROR","msg":"...","ts":"2024-01-15T10:23:00Z","svc":"auth"}
LOG_FILE="/var/log/app/app.log"
LEVEL="ERROR"
echo "=== $LEVEL entries in $LOG_FILE ==="
jq -r --arg lvl "$LEVEL" \
'select(.level == $lvl) | "[\(.ts)] [\(.svc)] \(.msg)"' \
"$LOG_FILE"
# Count errors per service
echo ""
echo "=== Error count by service ==="
jq -r --arg lvl "$LEVEL" \
'select(.level == $lvl) | .svc' "$LOG_FILE" \
| sort | uniq -c | sort -rnSprawdzenie wiedzy: działanie select() w jq
Sprawdź swoją wiedzę na temat działania select() wewnątrz potoku jq.
Rozważ następujące polecenie:
echo '[{"name":"Alice","age":30},{"name":"Bob","age":17},{"name":"Carol","age":22}]' | jq '[.[] | select(.age >= 18) | .name]'Jaki będzie wynik?
Podsumowanie lekcji: potoki jq do filtrowania JSON
Omówiono podstawowe narzędzia jq do nawigowania po danych JSON i filtrowania ich z poziomu wiersza poleceń:
- Notacja kropkowa (
.field,.a.b.c) wybiera pola z obiektów - Dostęp do tablic (
.[0],.[]) umożliwia odwoływanie się do elementów tablic i iterowanie po nich - Operator potoku (
|) łączy filtry; każdy etap przetwarza wszystkie wartości z poprzedniego etapu - select(cond) filtruje wartości, przekazując tylko te, dla których warunek ma wartość logiczną true
- Tworzenie obiektów i tablic (
{},[],map()) przekształca dane w nowe struktury - Operator alternatywy (
//) dostarcza wartości domyślne dla brakujących pól lub pól o wartości null - Flaga -r usuwa cudzysłowy przy przypisywaniu do zmiennej powłoki; --arg bezpiecznie wstrzykuje zmienne powłoki
- Rekurencyjne przeszukiwanie (
.. | .field?) przeszukuje głęboko zagnieżdżone drzewa, gdy ścieżka jest nieznana
Za pomocą tych elementów mogą Państwo przekształcić dowolną odpowiedź API w formacie JSON, plik dziennika lub konfigurację w dokładnie takie dane, jakich potrzebują skrypty — bez opuszczania terminala.
Często zadawane pytania
Czy lekcja „Filtrowanie i wybieranie JSON za pomocą potoków jq” jest bezpłatna?
Tak — pełny tekst „Filtrowanie i wybieranie JSON za pomocą potoków jq” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu DevOps Bootcamp, przejdź na CoddyKit PRO. Kurs DevOps Bootcamp zawiera 4 lekcji w sumie.
Co nauczysz się w „Filtrowanie i wybieranie JSON za pomocą potoków jq”?
Nawiguj po zagnieżdżonych obiektach i tablicach za pomocą selektorów jq, potoków oraz filtra select. Ćwiczysz DevOps Bootcamp z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć DevOps Bootcamp?
Nie wymagamy żadnego doświadczenia. DevOps Bootcamp w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 1 z 4.
Ile czasu zajmuje lekcja „Filtrowanie i wybieranie JSON za pomocą potoków jq”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji DevOps Bootcamp?
Tak. Każda lekcja DevOps Bootcamp zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- Filtrowanie i wybieranie JSON za pomocą potoków jq
- Przekształcanie i tworzenie obiektów JSON za pomocą jq
- Korzystanie z REST API za pomocą curl i jq
- Edycja plików konfiguracyjnych YAML za pomocą yq