Xử lý JSON và YAML: Các kỹ thuật jq và yq thực tế cho Linux

Linux tutorial - IT technology blog
Linux tutorial - IT technology blog

Cơn ác mộng của dữ liệu cấu trúc trên Terminal

Tôi vẫn nhớ một sự cố lúc 3 giờ sáng khi phải tìm một phiên bản container image cụ thể nằm sâu trong file deployment Kubernetes dài 2.400 dòng. Tôi đã thử dùng grep và awk, nhưng cấu trúc lồng nhau (nested) của YAML đã biến nó thành một cơn ác mộng. Chỉ một thay đổi nhỏ về thụt đầu dòng (indentation) cũng làm hỏng hoàn toàn regular expression của tôi. Nếu bạn đã từng phải nhìn chằm chằm vào một “bức tường” JSON nén (minified) dung lượng 50KB từ kết quả của lệnh curl, bạn sẽ hiểu cảm giác ức chế đó.

Các quy trình DevOps và hệ thống backend hiện nay đều chạy trên JSON và YAML. Chúng ta làm việc với chúng trong output của AWS CLI, các file Docker Compose và GitHub Actions. Quản lý dữ liệu này bằng các công cụ xử lý văn bản thông thường giống như việc cố gắng ăn súp bằng nĩa vậy. Đơn giản là bạn đang dùng sai công cụ.

Tại sao các công cụ truyền thống thất bại

Các tiện ích tiêu chuẩn như grep, sedawk hoạt động theo từng dòng (line-oriented). Chúng coi file của bạn là một chuỗi các ký tự phân tách bởi dấu xuống dòng. Tuy nhiên, JSON và YAML lại có cấu trúc dạng cây (tree-oriented). Một key tên là “status” có thể xuất hiện 15 lần trong các object lồng nhau khác nhau. Chạy lệnh grep "status" sẽ trả về cho bạn 15 dòng output mà không có bất kỳ ngữ cảnh nào về việc chúng thuộc về đâu.

Các file nén (minified) tạo ra một trở ngại khác. Các bộ parser JSON không quan tâm đến khoảng trắng, nhưng grep thì có. Một file chỉ có một dòng và một file có 100 dòng được thụt lề có thể giống hệt nhau đối với ứng dụng, nhưng chúng lại làm hỏng các script parse thủ công. Việc thiếu khả năng nhận diện cấu trúc là lý do tại sao regex thủ công thường thất bại khi schema thay đổi dù chỉ một chút.

Lựa chọn hướng đi đúng đắn

Khi bạn cần xử lý các định dạng này, thường có ba lựa chọn:

  • Script thủ công (Python/Node.js): Bạn có thể viết một script sử dụng json.load(). Nó hoạt động tốt, nhưng viết 10 dòng code chỉ để kiểm tra một giá trị duy nhất thì thật chậm chạp và kém hiệu quả.
  • Công cụ Linux tiêu chuẩn: Như đã đề cập, grepsed rất dễ bị lỗi. Chúng thường thất bại với các dữ liệu lồng nhau phức tạp.
  • Các bộ parser chuyên dụng (jq và yq): Những công cụ này hiểu cú pháp dữ liệu. Chúng coi các file là những object có thể tìm kiếm được thay vì chỉ là văn bản thuần túy.

Thay vì viết một script Python mất 2 giây để khởi tạo, jq (được viết bằng C) có thể xử lý một file JSON 10MB trong khoảng 0,1 giây. Trên một server Ubuntu thực tế với RAM 4GB hạn chế, hiệu quả này cực kỳ quan trọng. Nó cho phép bạn truy vấn dữ liệu bằng cú pháp tương tự như CSS selectors.

Cài đặt

Hầu hết các bản phân phối không cài đặt sẵn các công cụ này, nhưng chúng có sẵn trong các kho lưu trữ tiêu chuẩn. Trên Ubuntu hoặc Debian, việc cài đặt chỉ mất vài giây:

sudo apt update
sudo apt install jq -y

# Đối với yq, phiên bản của Mike Farah là bản tiêu chuẩn
sudo wget https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 -O /usr/bin/yq
sudo chmod +x /usr/bin/yq

Sử dụng jq để xử lý JSON

Bước đầu tiên với jq thường chỉ là làm cho dữ liệu dễ đọc hơn. Nếu bạn nhận được một phản hồi nén từ GitHub API, hãy chạy lệnh:

curl -s https://api.github.com/repos/stedolan/jq/commits?per_page=1 | jq '.'

Filter '.' là công cụ đơn giản nhất. Nó nhận đầu vào và xuất ra chính xác như vậy, nhưng có thêm định dạng (formatting) và màu sắc (color coding).

Trích xuất các giá trị cụ thể

Hãy đi sâu vào chi tiết. Nếu bạn có một object JSON cho người dùng và chỉ cần email của họ, hãy sử dụng ký hiệu dấu chấm. Nó giúp truy xuất qua các cấp bậc cấu trúc một cách nhanh chóng:

# Đầu vào: {"id": 1, "profile": {"email": "[email protected]"}}
echo '{"id": 1, "profile": {"email": "[email protected]"}}' | jq '.profile.email'

Mảng và Lọc

Mảng có vẻ phức tạp hơn, nhưng jq xử lý chúng rất mượt mà. Giả sử bạn cần tên của tất cả người dùng hiện đang ở trạng thái “active”:

# Ví dụ: [{"name": "Alice", "active": true}, {"name": "Bob", "active": false}]
cat users.json | jq '.[] | select(.active == true) | .name'

Toán tử .[] sẽ lặp qua mảng. Sau đó, select() lọc các item, và .name lấy ra giá trị cuối cùng.

Sử dụng yq để xử lý YAML

Chuyển sang YAML rất dễ dàng vì yq sử dụng cú pháp rất giống với jq. Nếu bạn đang kiểm tra file docker-compose.yml để xem các service đang sử dụng image nào, hãy chạy lệnh này:

yq '.services[].image' docker-compose.yml

Chỉnh sửa file trực tiếp

Chỉnh sửa là nơi yq thực sự tỏa sáng. Tôi thường dùng nó trong các pipeline CI/CD để cập nhật tag của image trước khi deployment. Nó giúp tránh việc tạo ra các file tạm lộn xộn:

yq -i '.services.web.image = "my-app:v2.0.5"' docker-compose.yml

Flag -i sửa đổi file trực tiếp. Điều này an toàn hơn nhiều so với việc dùng sed, vốn có thể vô tình thay thế nhầm chuỗi ký tự ở một vị trí khác trong file.

Quy trình làm việc đa định dạng

Đôi khi bạn có dữ liệu YAML nhưng API của bạn lại yêu cầu JSON. Bạn có thể kết hợp các công cụ này với nhau để thu hẹp khoảng cách đó. Nó giúp các định dạng khác nhau hoạt động đồng nhất.

# Chuyển đổi YAML sang JSON, sau đó trích xuất một giá trị cụ thể
yq -o=json eval config.yaml | jq '.database.port'

Đôi khi Terminal là chưa đủ

Các công cụ terminal rất hiệu quả, nhưng đôi khi một giao diện trực quan sẽ giúp ích nhiều hơn. Điều này đúng khi bạn cần debug các cấu trúc phức tạp cùng với đồng nghiệp. Nó cũng hữu ích khi cần kiểm tra (validate) các đoạn mã copy từ những file log lộn xộn. Trong những trường hợp đó, tôi sử dụng ToolCraft.

Công cụ JSON Formatter & Validator của họ rất tuyệt vời vì nó chạy hoàn toàn trong trình duyệt của bạn. Vì nó hoạt động ở phía client (client-side), dữ liệu thực tế của bạn không bao giờ được gửi lên server của họ. Nếu bạn cần chuyển đổi định dạng, YAML ↔ JSON Converter sẽ nhanh hơn việc tra cứu các flag của yq khi bạn đang vội.

Lời kết

Học cách sử dụng jqyq giúp bạn tiết kiệm hàng giờ tìm kiếm thủ công. Hãy bắt đầu với việc tra cứu các key cơ bản. Khi đã quen, hãy khám phá các hàm map()reduce(). Những công cụ này cho phép bạn xử lý hạ tầng như code (infrastructure as code) với độ chính xác thực sự. Một khi bạn ngừng coi JSON là văn bản thuần túy, dòng lệnh của bạn sẽ trở nên mạnh mẽ hơn nhiều.

Share: