การกรองและเลือก JSON ด้วยไปป์ไลน์ jq
เข้าถึงออบเจกต์ซ้อนกันและอาร์เรย์ด้วยตัวเลือก ท่อ และตัวกรอง select ของ jq
การกรองและเลือก JSON ด้วยไปป์ไลน์ jq เป็นบทเรียน DevOps Bootcamp ฟรีบน CoddyKit นี่คือบทเรียนที่ 1 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน DevOps Bootcamp และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส DevOps Bootcamp มีบทเรียนทั้งหมด 4 บทเรียน
jq คืออะไร และเหตุใดจึงควรใช้
jq เป็นเครื่องมือบรรทัดคำสั่งที่มีขนาดเล็กแต่ทรงพลังสำหรับแยกวิเคราะห์ กรอง และแปลงข้อมูล JSON โดยเป็นเสมือน sed สำหรับ JSON — ส่ง JSON เข้าไปแล้วรับผลลัพธ์ที่มีโครงสร้างกลับมา
- ติดตั้งไว้ล่วงหน้าในดิสทริบิวชัน Linux ส่วนใหญ่ หรือพร้อมติดตั้งผ่าน
apt install jq/brew install jq - ทำงานร่วมกับไปป์ไลน์ของเชลล์ได้อย่างราบรื่นกับ
curlcatและเครื่องมืออื่น ๆ - รองรับการกรอง การแมป การลดรูป และการแปลงรูปแบบ
รูปแบบการเรียกใช้พื้นฐานคือ jq '<filter>' file.json หรือส่งผ่านไปป์ด้วย cat file.json | jq '<filter>' ตัวกรอง . (จุด) คือเอกลักษณ์ ซึ่งจะแสดงเอกสารทั้งฉบับในรูปแบบอ่านง่าย
# Pretty-print a JSON file
jq '.' data.json
# Or pipe from curl
curl -s https://api.github.com/users/torvalds | jq '.'การเลือกฟิลด์ของออบเจ็กต์ด้วยสัญกรณ์จุด
หากต้องการเข้าถึงฟิลด์ในออบเจ็กต์ JSON ให้ใช้สัญกรณ์จุด: .fieldName คุณสามารถต่อเซเล็กเตอร์เพื่อเข้าถึงออบเจ็กต์ที่ซ้อนกันได้
.name— ฟิลด์ระดับบนสุด.address.city— ฟิลด์ที่ซ้อนกัน."field-with-dash"— ฟิลด์ที่มีอักขระพิเศษต้องใส่อัญประกาศ
หากไม่มีฟิลด์นั้น jq จะคืนค่า null แทนการแจ้งข้อผิดพลาด จึงใช้งานในสคริปต์ได้อย่างปลอดภัยโดยไม่ต้องตรวจสอบค่า null เพิ่มเติมสำหรับฟิลด์ที่อาจมีหรือไม่มีก็ได้
# 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"การเข้าถึงสมาชิกอาร์เรย์และการวนซ้ำ
เข้าถึงอาร์เรย์ JSON ด้วยสัญกรณ์วงเล็บเหลี่ยม jq ใช้ดัชนีที่เริ่มจากศูนย์
.items[0]— สมาชิกตัวแรก.items[-1]— สมาชิกตัวสุดท้าย.items[1:3]— ส่วนย่อย (ตั้งแต่ดัชนี 1 จนถึงก่อนดัชนี 3).items[]— แยกอาร์เรย์: ส่งออกสมาชิกแต่ละตัวเป็นค่าแยกกัน (นี่คือตัววนซ้ำ)
ตัววนซ้ำ [] เป็นพื้นฐานสำคัญของไปป์ไลน์ jq เพราะช่วยให้ใช้ตัวกรองถัดไปกับสมาชิกแต่ละตัวได้อย่างอิสระ
# 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"การสร้างไปป์ไลน์ jq ด้วยตัวดำเนินการไปป์
เช่นเดียวกับไปป์ของเชลล์ | jq มีตัวดำเนินการไปป์ภายในของตนเอง ซึ่งส่งผลลัพธ์จากตัวกรองหนึ่งไปเป็นข้อมูลนำเข้าของตัวกรองถัดไป
jq '.users[] | .name'— วนซ้ำสมาชิกผู้ใช้ แล้วดึงชื่อของแต่ละคนjq '.data | .items[] | .id'— เข้าถึงข้อมูล แยกสมาชิก items แล้วดึง id
ไปป์ภายในนิพจน์ jq ช่วยให้สร้างการแปลงข้อมูลที่ซับซ้อนทีละขั้น แต่ละขั้นจะได้รับทุกค่าที่ขั้นก่อนหน้าสร้างขึ้น รวมถึงหลายค่าจากตัววนซ้ำ
ข้อสังเกตสำคัญ: เมื่อตัววนซ้ำสร้างค่า N ค่า ตัวกรองปลายทางทุกตัวจะทำงาน N ครั้ง ครั้งละหนึ่งค่า
# 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
# 25การกรองด้วย select()
ตัวกรอง select(condition) จะส่งค่าต่อเมื่อเงื่อนไขเป็นจริงเท่านั้น หากไม่เป็นจริงจะไม่สร้างผลลัพธ์ใด ๆ ซึ่งเทียบเท่ากับ grep ใน jq หรือ WHERE ใน SQL
select(.age > 18)— เก็บออบเจ็กต์ที่อายุสูงกว่า 18select(.status == "active")— ตรวจสอบความเท่ากันselect(.name | startswith("A"))— การทดสอบสตริงที่ซ้อนกัน
ใช้ select ร่วมกับตัววนซ้ำเพื่อกรองอาร์เรย์: .items[] | select(.active) จะส่งออกเฉพาะสมาชิกที่ .active เป็นค่าจริง
# 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"การสร้างออบเจ็กต์และอาร์เรย์ขึ้นใหม่ด้วย {} และ []
jq ช่วยปรับรูปทรงข้อมูลได้ด้วยการสร้างออบเจ็กต์ใหม่ด้วย {} และอาร์เรย์ใหม่ด้วย []
{name: .name, city: .address.city}— เลือกและเปลี่ยนชื่อฟิลด์ลงในออบเจ็กต์ใหม่[.items[] | .id]— รวบรวมค่าที่วนซ้ำกลับมาเป็นอาร์เรย์- รูปแบบย่อ:
{name, age}เทียบเท่ากับ{name: .name, age: .age}
การครอบไปป์ไลน์ด้วย [...] เรียกว่าการสร้างอาร์เรย์ และจำเป็นเมื่อคุณต้องการผลลัพธ์เป็นอาร์เรย์ JSON แทนที่จะเป็นกระแสของค่า
# 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"]การทำงานกับอาร์เรย์ซ้อนกันและการไล่สำรวจแบบเวียนเกิด
JSON ในการใช้งานจริงมักซ้อนกันหลายชั้น jq มีเครื่องมือสองอย่างสำหรับการนำทางเชิงลึก:
.a.b.c— เส้นทางที่ระบุชัดเจนเมื่อทราบโครงสร้าง.. | .fieldName?— การไล่สำรวจแบบเวียนเกิด: เดินผ่านทุกโหนดในต้นไม้และส่งออกค่าที่มีคีย์ดังกล่าวอยู่
ตัวดำเนินการ ? (ลองทำ) จะระงับข้อผิดพลาดเมื่อไม่มีฟิลด์อยู่ที่โหนดนั้น ซึ่งสำคัญอย่างยิ่งเมื่อใช้การไล่สำรวจแบบเวียนเกิดกับต้นไม้ที่มีโครงสร้างหลากหลาย
ใช้การไล่สำรวจแบบเวียนเกิดกับเอกสารขนาดใหญ่อย่างประหยัด เพราะจะเยี่ยมชมทุกโหนดและอาจทำงานช้า หากโครงสร้างคาดเดาได้ ควรเลือกใช้เส้นทางที่ระบุชัดเจน
# 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
# 2ตัวอย่างเชิงปฏิบัติ: การแยกวิเคราะห์การตอบกลับ API จาก curl
กรณีใช้งาน jq ที่พบบ่อยที่สุดอย่างหนึ่งคือการแยกวิเคราะห์การตอบกลับ REST API ที่ดึงมาด้วย curl การใช้ curl -s (ทำงานแบบเงียบ) ร่วมกับไปป์ไลน์ jq ช่วยให้ดึงข้อมูลได้สะอาดและนำไปใช้ในสคริปต์ได้
- ดึงค่าหนึ่งค่า:
curl -s URL | jq '.field' - สร้างตารางสรุป: วนซ้ำอาร์เรย์ แล้วสร้างออบเจ็กต์ใหม่โดยเก็บไว้เฉพาะฟิลด์ที่ต้องการ
- ใช้
-r(ผลลัพธ์ดิบ) เพื่อลบเครื่องหมายอัญประกาศที่ครอบค่าสตริงออก ซึ่งจำเป็นเมื่อกำหนดค่าให้ตัวแปรเชลล์
เคล็ดลับ: เพิ่ม -r เสมอเมื่อจะนำผลลัพธ์จาก jq ไปใช้เป็นตัวแปรเชลล์หรือส่งต่อให้คำสั่งอื่น
#!/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"การใช้ map() และ map_values()
jq มีฟังก์ชันลำดับสูงที่สะดวกสองอย่างสำหรับแปลงคอลเลกชัน:
map(f)— ใช้ตัวกรองfกับสมาชิกทุกตัวของอาร์เรย์ แล้วคืนค่าเป็นอาร์เรย์ใหม่ เทียบเท่ากับ[.[] | f]map_values(f)— ใช้fกับค่าทุกค่าในออบเจ็กต์หรืออาร์เรย์ โดยคงคีย์หรือดัชนีไว้
ฟังก์ชันเหล่านี้อ่านเข้าใจง่ายกว่าการครอบไปป์ไลน์ด้วย [] ด้วยตนเอง และเป็นรูปแบบ jq ที่เหมาะสำหรับการแปลงข้อมูลซึ่งควรคงรูปเป็นอาร์เรย์
# 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}การจัดการฟิลด์ที่อาจมีหรือไม่มีก็ได้และค่าเริ่มต้นด้วย //
ข้อมูล JSON จากแหล่งภายนอกมักไม่สม่ำเสมอ ฟิลด์อาจหายไปหรือมีค่า null jq มีตัวดำเนินการทางเลือก // (เครื่องหมายทับสองตัว) เพื่อกำหนดค่าเริ่มต้น
.nickname // "anonymous"— ใช้.nicknameหากไม่ใช่ null หรือ false มิฉะนั้นใช้"anonymous".count // 0— ค่าเริ่มต้นเชิงตัวเลข- ใช้ร่วมกับ
select:select((.status // "inactive") == "active")
วิธีนี้กระชับกว่าวิธีเทียบเท่าในเชลล์อย่าง ${VAR:-default} มาก และนำไปประกอบใช้ภายในไปป์ไลน์ที่ยาวขึ้นได้อย่างราบรื่น
# 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: 0สคริปต์เชิงปฏิบัติ: ตัวแยกวิเคราะห์บันทึก JSON
การบันทึกข้อมูลในรูปแบบ JSON ที่มีโครงสร้างเป็นมาตรฐานของระบบสมัยใหม่ ต่อไปนี้คือสคริปต์ที่ใช้งานได้จริง โดยอ่านไฟล์บันทึก JSON ที่แบ่งบรรทัดละหนึ่งรายการ กรองรายการที่เป็นข้อผิดพลาด และจัดรูปแบบเป็นสรุปที่มนุษย์อ่านได้
รูปแบบสำคัญที่ใช้:
-c(ผลลัพธ์แบบกระชับ) — หนึ่งออบเจ็กต์ JSON ต่อบรรทัด เหมาะสำหรับส่งต่อให้ลูปของเชลล์--arg name value— แทรกตัวแปรเชลล์เป็นอาร์กิวเมนต์สตริงของ jqselectสำหรับกรองตามระดับของบันทึก-rสำหรับผลลัพธ์สตริงดิบที่เหมาะกับecho
#!/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 -rnตรวจสอบความเข้าใจ: พฤติกรรมของ select() ใน jq
ทดสอบความเข้าใจเกี่ยวกับการทำงานของ select() ภายในกระบวนการต่อเนื่องของ jq
กำหนดให้มีคำสั่งต่อไปนี้:
echo '[{"name":"Alice","age":30},{"name":"Bob","age":17},{"name":"Carol","age":22}]' | jq '[.[] | select(.age >= 18) | .name]'ผลลัพธ์จะเป็นอย่างไร
สรุปบทเรียน: กระบวนการต่อเนื่องของ jq สำหรับกรอง JSON
คุณได้เรียนรู้เครื่องมือหลักของ jq สำหรับนำทางและกรอง JSON จากบรรทัดคำสั่งแล้ว:
- สัญกรณ์จุด (
.field,.a.b.c) ใช้เลือกฟิลด์จากออบเจ็กต์ - การเข้าถึงอาร์เรย์ (
.[0],.[]) ใช้ระบุดัชนีและวนซ้ำผ่านอาร์เรย์ - ตัวดำเนินการไปป์ (
|) ใช้เชื่อมตัวกรองเข้าด้วยกัน โดยแต่ละขั้นตอนจะประมวลผลค่าทั้งหมดจากขั้นตอนก่อนหน้า - select(cond) ใช้กรองค่า โดยส่งต่อเฉพาะค่าที่เงื่อนไขเป็นจริง
- การสร้างออบเจ็กต์/อาร์เรย์ (
{},[],map()) ใช้ปรับรูปข้อมูลให้เป็นโครงสร้างใหม่ - ตัวดำเนินการทางเลือก (
//) ใช้กำหนดค่าเริ่มต้นให้ฟิลด์ที่เป็นค่าว่างหรือไม่มีอยู่ - แฟล็ก -r ใช้ตัดเครื่องหมายคำพูดสำหรับกำหนดค่าให้ตัวแปรเชลล์ ส่วน --arg ใช้แทรกตัวแปรเชลล์อย่างปลอดภัย
- การไล่สำรวจแบบเรียกซ้ำ (
.. | .field?) ใช้ค้นหาในโครงสร้างต้นไม้ที่ซ้อนกันลึก เมื่อไม่ทราบเส้นทาง
ด้วยองค์ประกอบเหล่านี้ คุณสามารถแปลงการตอบกลับจาก API ที่เป็น JSON ไฟล์บันทึก หรือการกำหนดค่า ให้เป็นข้อมูลตรงตามที่สคริปต์ต้องการได้ โดยไม่ต้องออกจากเทอร์มินัล
คำถามที่พบบ่อย
บทเรียน “การกรองและเลือก JSON ด้วยไปป์ไลน์ jq” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “การกรองและเลือก JSON ด้วยไปป์ไลน์ jq” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส DevOps Bootcamp ให้อัปเกรดเป็น CoddyKit PRO คอร์ส DevOps Bootcamp มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “การกรองและเลือก JSON ด้วยไปป์ไลน์ jq”
เข้าถึงออบเจกต์ซ้อนกันและอาร์เรย์ด้วยตัวเลือก ท่อ และตัวกรอง select ของ jq คุณปฏิบัติ DevOps Bootcamp ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน DevOps Bootcamp หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน DevOps Bootcamp บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 1 จากทั้งหมด 4 บทเรียน
บทเรียน “การกรองและเลือก JSON ด้วยไปป์ไลน์ jq” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน DevOps Bootcamp นี้ได้ไหม
ได้ บทเรียน DevOps Bootcamp ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- การกรองและเลือก JSON ด้วยไปป์ไลน์ jq
- การแปลงและสร้างออบเจกต์ JSON ด้วย jq
- การใช้ REST API ด้วย curl และ jq ร่วมกัน
- การแก้ไขไฟล์การกำหนดค่า YAML ด้วย yq