JC, chuyển đầu ra của các công cụ dòng lệnh phổ biến sang JSON
(github.com/kellyjonbrazil)- JC là công cụ giúp chuyển đầu ra của nhiều công cụ CLI, định dạng tệp và chuỗi văn bản thông thường sang JSON để dễ phân tích hơn trong script
- Công cụ nhận đầu vào từ pipe và xuất JSON ra
STDOUT, đồng thời hỗ trợ magic syntax khi đặt trước lệnh nhưjc dig example.com - Đầu ra mặc định dùng schema nghiêm ngặt theo từng parser, chuyển đổi số,
null, boolean và các trường ngữ nghĩa bổ sung; có thể truy cập JSON thô trước tiền xử lý bằng-rhoặcraw=True - Có thể dùng như thư viện Python, nhận kết quả dưới dạng dictionary Python, danh sách dictionary hoặc iterable trì hoãn thay vì JSON
- Với pipeline lớn hoặc chạy dài, công cụ cung cấp streaming parser xuất JSON Lines/NDJSON,
--slurp,--meta-out, plugin parser cục bộ và tùy chọn tắt cảnh báo tương thích nền tảng
JC làm gì
- JC(JSON Convert) chuyển đầu ra của nhiều công cụ CLI, định dạng tệp và chuỗi văn bản thông thường sang JSON
- Đầu ra đã chuyển đổi có thể được pipe sang các công cụ như
jqhoặcjellođể xử lý tiếp - Ví dụ
dig example.com | jc --digchuyển đầu radigthành một mảng JSON, sau đójq -r '.[].answer[].data'chỉ trích xuất địa chỉ IP - Cùng tác vụ đó cũng có thể chạy bằng magic syntax như
jc dig example.com | jq -r '.[].answer[].data'
Cách dùng CLI và thư viện Python
- Cách dùng CLI cơ bản là nhận đầu vào qua pipe rồi xuất biểu diễn JSON
COMMAND | jc [SLICE] [OPTIONS] PARSERcat FILE | jc [SLICE] [OPTIONS] PARSERecho STRING | jc [SLICE] [OPTIONS] PARSER
- magic syntax có dạng
jc [SLICE] [OPTIONS] COMMANDhoặcjc [SLICE] [OPTIONS] /proc/<path-to-procfile>, tức đặtjctrước lệnh hoặc tệp/proc- Không hỗ trợ alias của lệnh và shell builtin
- Trong Python có thể gọi như
jc.parse('dig', cmd_output)- Giá trị trả về không phải chuỗi JSON mà có thể là dictionary Python, danh sách dictionary hoặc iterable trì hoãn của streaming parser
- Tài liệu gói Python có thể xem tại
help('jc'),help('jc.lib')hoặc tài liệu trực tuyến
Biểu diễn đầu ra và schema
- Biểu diễn mặc định dùng schema nghiêm ngặt theo từng parser
- Các số đã biết được chuyển thành giá trị JSON
inthoặcfloat - Các giá trị
Noneđã biết được chuyển thành JSONnull - Các giá trị boolean đã biết cũng được chuyển đổi
- Một số parser bổ sung thêm các trường ngữ nghĩa phụ
- Các số đã biết được chuyển thành giá trị JSON
- Có thể truy cập JSON thô trước tiền xử lý bằng tùy chọn
-rcủa CLI hoặcparse(..., raw=True)trong thư viện Python - Schema của từng parser có thể xem qua các liên kết tài liệu cạnh danh sách parser
- Đầu ra JSON mặc định ở dạng compact, có thể dùng tùy chọn
-pđể xuất pretty format - Cũng có thể xuất YAML bằng tùy chọn
-yhoặc--yaml-out
Parser được hỗ trợ và ví dụ sử dụng
- Các parser được hỗ trợ gồm lệnh CLI, định dạng tệp và parser chuỗi
- Danh sách parser trong README bao gồm
dig,ls,ping,ps,netstat,ifconfig,csv,xml,yaml,/etc/hosts,/etc/passwd,/proc/,systemctl,git log,jwt,url,semver, các tệp liên quan đếnx509và hơn thế nữa - Các ví dụ đầu ra cho thấy cách nhiều đầu vào được chuyển thành cấu trúc JSON
- Đầu ra
arpđược chuyển thành các trường địa chỉ, loại phần cứng, địa chỉ MAC và giao diện - Tệp CSV được chuyển thành mảng đối tượng với header làm key
/etc/hostsđược chuyển thành IP và mảng hostnameifconfigđược chuyển thành đối tượng gồm tên giao diện, MTU, địa chỉ IPv4/IPv6, bộ đếm packet và byte, v.v.pingđược chuyển thành đối tượng gồm số packet gửi/nhận, tỷ lệ mất gói, thời gian khứ hồi và danh sách phản hồi
- Đầu ra
- Trong Ansible, công cụ có thể được dùng như filter plugin của collection
community.general
Cách cài đặt
jccó thể được cài theo nhiều cáchpip3 install jc- Kho gói của hệ điều hành
- Binary theo kiến trúc từ GitHub Releases
- Ví dụ cài qua kho gói của hệ điều hành gồm
- Debian/Ubuntu:
apt-get install jc - Fedora:
dnf install jc - openSUSE:
zypper install jc - Arch:
pacman -S jc - macOS:
brew install jc - FreeBSD: cài qua ports
- Ansible filter plugin:
ansible-galaxy collection install community.general - FortiSOAR connector: cài từ FortiSOAR Connector Marketplace
- Debian/Ubuntu:
Các tùy chọn chính
-a/--about: xuất thông tin vềjcvà parser dưới dạng JSON hoặc YAML-d/--debug: xuất thông điệp truy vết khi có vấn đề phân tích;-ddcho mức debug chi tiết hơn-h/--help: hiển thị trợ giúp, và có thể xem tài liệu parser bằngjc -h --parser_name-M/--meta-out: thêm metadata như timestamp, tên parser, magic command, mã thoát của magic command vào đầu ra-q/--quiet: ẩn thông điệp cảnh báo của parser;-qqsẽ bỏ qua lỗi của streaming parser-s/--slurp: gom nhiều dòng đầu vào thành một mảng-u/--unbuffer: tắt output buffering-B/--bash-comp,-Z/--zsh-comp: tạo script completion cho Bash hoặc Zsh
Slice và Slurp
- Slice xử lý chỉ một phần dòng đầu vào bằng cú pháp
START:STOPtương tự Python slicing - Ví dụ, với bảng có header và footer, có thể dùng
jc 1:-1 --asciitablehoặcjc 1:4 --asciitableđể chỉ chuyển dữ liệu ở giữa sang JSON - Slice dương và slice rỗng có hiệu quả bộ nhớ tốt nhất, còn slice âm dùng nhiều bộ nhớ hơn
- Slurp là tính năng cho phép các parser chuỗi nhận đầu vào một dòng xử lý nhiều mục cùng lúc rồi xuất thành mảng
- Ví dụ: tệp chứa nhiều địa chỉ IP, mỗi dòng một địa chỉ, có thể xử lý bằng
jc --slurp --ip-address
- Ví dụ: tệp chứa nhiều địa chỉ IP, mỗi dòng một địa chỉ, có thể xử lý bằng
- Magic syntax cho
/proctự động hỗ trợ slurp khi chọn nhiều tệp- Khi chuyển nhiều tệp
/proc, mỗi đối tượng kết quả sẽ có thêm trường_fileđể biết nó tương ứng với tệp nào
- Khi chuyển nhiều tệp
- Nếu dùng
--meta-outcùng slurp, cấu trúc đầu ra sẽ được bọc bằng mảngresultvà đối tượng_jc_meta
Mã thoát và metadata
- Lỗi nghiêm trọng bên trong
jctạo ra mã thoát100, nếu không sẽ trả về0 - Khi dùng magic syntax,
jclưu mã thoát của chương trình được phân tích và cộng vào mã thoát củajc- Nếu
ifconfiglà1vàjclà0thì mã thoát kết hợp là1 - Nếu
ifconfiglà0vàjclà100thì mã thoát kết hợp là100 - Nếu cả hai đều lỗi thì mã thoát kết hợp là
101
- Nếu
- Khi dùng
--meta-outhoặc-Mvới magic syntax, đối tượng_jc_metasẽ chứa thông tin magic command và mã thoát
Màu sắc và biến môi trường
- Có thể chỉ định màu cho key name, keyword, number và string bằng biến môi trường
JC_COLORS- Cú pháp là
JC_COLORS=<keyname_color>,<keyword_color>,<number_color>,<string_color> - Giá trị màu có thể là
black,red,green,yellow,blue,magenta,cyan,gray,brightblack,brightred,brightgreen,brightyellow,brightblue,brightmagenta,brightcyan,white,default
- Cú pháp là
- Nếu thiết lập biến môi trường
NO_COLORthì đầu ra màu sẽ bị tắt - Tùy chọn
-Ccó ưu tiên cao hơn biến môi trườngNO_COLORvà tùy chọn-mđể buộc bật đầu ra màu
Streaming parser
- Phần lớn parser đọc toàn bộ
STDINvào bộ nhớ, phân tích rồi tuần tự xuất tài liệu JSON - Một số streaming parser xử lý theo từng dòng ngay khi nhận đầu vào và xuất theo JSON Lines hoặc NDJSON
- Ví dụ:
ls-s,ping-s - Với các đầu ra lớn như
ls -lR /, cách này giúp giảm mạnh lượng bộ nhớ sử dụng và đôi khi còn nhanh hơn
- Ví dụ:
- Streaming parser không thể dùng cùng magic syntax
- Trong pipeline chạy dài, để lỗi phân tích không làm vỡ pipe, có thể dùng
-qqhoặcignore_exceptions=Truetrong Python- Các dòng thành công sẽ có
_jc_meta.success: true - Các dòng thất bại sẽ có
_jc_meta.success: false,error,line
- Các dòng thành công sẽ có
- Nếu đầu ra giữa các pipe hiện chậm do bộ đệm của hệ điều hành, có thể dùng
-uđể xuất không đệm- Tuy vậy, với luồng dữ liệu lớn, output không đệm có thể chậm hơn
- Trong Python, streaming parser nhận đầu vào iterable và trả về đối tượng iterable để xử lý trì hoãn
Plugin parser
- Parser plugin cục bộ có thể được đặt trong thư mục
jc/jcparserscủa thư mục dữ liệu ứng dụng- Linux/unix:
$HOME/.local/share/jc/jcparsers - macOS:
$HOME/Library/Application Support/jc/jcparsers - Windows:
$LOCALAPPDATA\\jc\\jc\\jcparsers
- Linux/unix:
- Plugin là tệp module Python tiêu chuẩn
- Có thể dùng
jc/parsers/foo.pyhoặcjc/parsers/foo_s.pylàm template - Tên tệp plugin phải là tên module Python hợp lệ, bắt đầu bằng chữ cái và chỉ gồm ký tự chữ-số cùng underscore
- Plugin cục bộ có thể override parser mặc định
Locale, múi giờ và khả năng tương thích
- Để có kết quả tốt nhất, nên đặt
LC_ALLthànhChoặcen_US.UTF-8 - Trên một số hệ thống cũ, locale
Ckhông hỗ trợ mã hóa UTF-8, khiến đầu ra UTF-8 bị hạ xuống ASCII và các chuỗi escape\\u - Một số parser bổ sung các trường timestamp epoch được tính toán
- Nếu tên trường không có hậu tố
_utcthì được xem là timestamp naive dựa trên múi giờ cục bộ - Nếu phát hiện múi giờ UTC trong văn bản đầu ra của lệnh thì timestamp sẽ trở thành timezone aware và key sẽ có hậu tố
_utc - Các múi giờ khác ngoài UTC không được hỗ trợ như timestamp aware
- Nếu tên trường không có hậu tố
- Một số parser chuyển đổi đầu ra theo từng nền tảng nên sẽ cảnh báo nếu chạy trên nền tảng không được hỗ trợ
- Ngay cả trên nền tảng không được hỗ trợ, vẫn có thể phân tích tệp đầu ra từ hệ thống khác; khi đó có thể ẩn cảnh báo bằng
-qhoặcquiet=Truetrong Python - Các nền tảng đã được kiểm thử gồm Centos 7.7, Ubuntu 18.04/20.04, Fedora32, macOS 10.11.6/10.14.6, NixOS, FreeBSD12, Windows 10, Windows 2016 Server, Windows 2019 Server
1 bình luận
Ý kiến trên Hacker News
Trên FreeBSD, vấn đề này đã được giải quyết phần nào bằng libxo:
Có thể nhận đầu ra có cấu trúc như
$ ps --libxo=json | jqNó không hoàn hảo: từng có hỗ trợ
lsnhưng đã bị gỡ bỏ vì lý do nào đó, và không phải tiện ích nào cũng hỗ trợjctrông như một giải pháp tạm thời tuyệt vời khi cung cấp parser cho nhiều lệnh, nhưng có hạn chế là phải parse đầu ra văn bản vốn không được thiết kế để parse ngay từ đầuSẽ tốt hơn nếu các tiện ích cùng hội tụ về việc xuất đầu ra có cấu trúc bằng một flag chung; dù việc đặt làm mặc định như PowerShell có thể là quá mức với Unix/Linux, chỉ cần một flag chuẩn
--jsonthôi cũng đã là bước tiến lớnhttps://wiki.freebsd.org/LibXo
https://reviews.freebsd.org/D13959
Theo tôi hiểu, nó không chỉ truyền dữ liệu có cấu trúc, mà còn đưa các đối tượng opaque qua pipeline và thậm chí có thể quay lại bước trước để gọi method: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_methods?view=powershell-7.4
Tôi cũng thích các wrapper như
jc,libxovà những shell thử nghiệm như https://www.nushell.sh/, nhưng chúng tập trung vào truyền dữ liệu hơn là các đối tượng có method thực thi đượcDữ liệu có cấu trúc vẫn cho cảm giác rất kiểu Unix, và nếu cần đối tượng thật sự thì theo tôi đã đến lúc bật Python hoặc Ruby
Dù shell có tuyệt vời đến đâu và tính năng lập trình có tốt đến đâu, điều quan trọng là phải biết khi nào nên chuyển từ shell script sang một ngôn ngữ lập trình đầy đủ
jcđược giới thiệu như một công cụ lấp vào vai trò đó trong lúc này, và có vẻ được chủ ý làm bước đệm cho đến khi hỗ trợ-j/--jsonlan rộng trong các công cụ Unix nói chunghttps://blog.kellybrazil.com/2019/11/26/bringing-the-unix-philosophy-to-the-21st-century/
/proctrả về dữ liệu JSON thay vì các file văn bản phi cấu trúcMột giải pháp có cấu trúc hơn có thể là cho phép xuất các cấu trúc dữ liệu từ ELF, serialize các cấu trúc dữ liệu đó thành đầu ra terminal, rồi để người dùng xuất hoặc xử lý theo định dạng mong muốn như JSON, YAML
Tôi không nhớ chính xác tiện ích nào, có lẽ là
iostat, nhưng nó từng định dạng các dòng JSON bằng nội suy chuỗi và tạo ra đầu ra hỏng hoàn toàn với một tổ hợp flag nhất địnhKhông biết giờ đã khá hơn chưa, nhưng khi có tùy chọn interval thì tôi kỳ vọng một dạng như JSON lines
Về mặt khả dụng, tôi nghĩ PowerShell và kubectl đi trước libxo rất xa
https://github.com/Juniper/libxo
https://libxo.readthedocs.io/en/latest/
Ý tưởng thật sự rất hay, nhưng nghĩ đến bảo trì thì thấy lo
Xét đến các thay đổi đầu ra theo phiên bản, flag của lệnh, v.v. thì trông như địa ngục bảo trì; dùng thực tế có thể ổn trong một số trường hợp, nhưng có lẽ sẽ nhanh chóng mất đi sự mới mẻ khi vượt quá các ví dụ cơ bản
Hơn nữa, việc dùng
--cho tùy chọn của công cụ cũng có vẻ không hayNếu mỗi parser mới lại cần một flag mới, trang trợ giúp hoặc man page có thể dài tới hàng nghìn dòng
Khi một tiện ích nào đó quyết định hỗ trợ xuất JSON riêng, từ đó công cụ này chỉ cần ủy quyền sang tính năng đó
jctrước lệnh, nhưjc lsTham số
--cmdcho phép xử lý dữ liệu trước khi chuyển đổi, nên thực ra là một ý tưởng hayVí dụ, có thể bạn muốn
grepdanh sách trước khi chuyển đổiVề mặt bảo trì, đầu ra của các lệnh Unix cơ bản nếu thay đổi lớn sẽ làm hỏng không chỉ công cụ này mà cả vô số script, nên có lẽ nó sẽ không hỏng thường xuyên vì cập nhật binary khác như ta nghĩ
Một bộ parser dùng chung được quản lý tốt thì tốt hơn các parser tạm bợ rải rác khắp nơi, nhưng trong những tình huống cần đầu ra JSON trực tiếp để làm việc phức tạp, tôi không muốn bản thân việc parse lại làm vấn đề tệ hơn
Để dùng nó một cách yên tâm, rốt cuộc có lẽ tôi chỉ cần gửi PR thêm test case cho mọi thứ mình định dùng
Dù sao thì các test đó tôi cũng đã phải tự viết
Chỉ còn cách mọi người gửi thông tin parse cho các công cụ cần thiết, và người dùng dễ dàng giữ chúng luôn cập nhật
Nushell có cách tiếp cận khác, nhưng phần lớn cũng đi đến cùng một điểm: dữ liệu có cấu trúc của lệnh shell
Chủ yếu là bản thân shell đảm nhận vai trò đó
http://www.nushell.sh/
from json, nên thực tế kết hợp khá tốt với JCTrước đây tôi đã quay một video giới thiệu vài tính năng hay của Nushell, và khoảng phút 19 có nói về việc kết hợp với
jc: https://www.youtube.com/watch?v=KF5dtxVsn1EKhi đang định viết một script quét các tệp trong thư mục khớp với một mẫu nhất định, rồi xóa những tệp có thời điểm sửa đổi cách nhau trong vòng 10 phút, tôi nhớ ra Nushell rất hợp với việc kiểu này
Thử nghịch một chút thì cuối cùng tôi cũng bắt được cảm giác, và giờ thì đang mê nó
Ngay cả dữ liệu không có cấu trúc, nếu có thể chuyển thành dạng như danh sách bản ghi để xử lý thì rất mạnh
Theo một nghĩa nào đó, việc tệp có ở khắp nơi là rất tuyệt, đó là lời hứa của Unix và được mở rộng hơn trong Plan 9
Nhưng các tệp không có cấu trúc, hoặc mỗi tệp lại có định dạng riêng, cũng kìm chân ta không kém
Chỉ riêng việc cố phân tích một tệp log nginx bằng những thứ như
awkcũng có thể đã phiền phứcMột nhược điểm lớn là rất khó thực hiện việc viết lại hệ thống quy mô lớn hay thay đổi thiết kế trong không gian người dùng Linux
Tôi muốn một shell thông minh hơn, tôi cũng thích tệp, và vẫn để cuốn sách
awkbên cạnh, nhưng tôi nghĩ đã đến lúc cần cải thiện nghiêm túc về phân tích dữ liệuGiá mà chương trình có thể tự quyết định có xuất dữ liệu có cấu trúc hay không, giống như cách chúng tự quyết định có render đầu ra màu hay không
Vì bất kỳ UI văn bản nào cũng có thể được dùng như API, văn bản dễ đọc cho con người được ưu tiên
Vì vậy shell scripting giống như tạo tiện ích mở rộng trình duyệt bên thứ ba
Nhìn rồi đoán, sau đó vội ghép một parser dựa trên phỏng đoán đó và cầu mong nó chạy ổn
Sẽ rất tốt nếu có một stdout tiêu chuẩn thứ ba dành cho nội dung máy đọc
Terminal mặc định không in ra nó; khi pipe thì đầu ra này được pipe; nó phải là JSONL; và trang man sẽ nêu rõ hợp đồng
Khi đó stdout tiêu chuẩn có thể dành cho con người, còn việc parse thì là thứ ta làm với nhận thức rằng tuy có thể parse nhưng mong manh
Tất nhiên đây là một ý tưởng phá vỡ hoàn toàn tương thích ngược, và nếu phi thực tế đến mức phát minh lại CLI từ nền tảng để hiện đại hóa nó, danh sách những thứ muốn thay đổi sẽ rất dài
Để có được cải tiến cũng thật sự khó, và khi người như Lennart cố dọn bỏ những cặn bã cũ kỹ hàng chục năm tuổi thì lập tức lại có drama
JSON cũng vẫn chưa hoàn hảo
JSON là một ý tưởng tạm ổn, nhưng để làm đúng thì cần một định dạng có thể bộc lộ những thứ như kiểu dữ liệu
Cần một cách gần với PowerShell hơn, để có thể làm những điều đáng kinh ngạc như coi số là số và tính chênh lệch ngày bằng
$a - $bSẽ rất tốt nếu điều này xảy ra trong GNU coreutils
Vấn đề là nó bám quá sâu vào .NET và object, nên rất khó tích hợp với các lệnh native hiện có trên bất kỳ nền tảng nào
Vì về bản chất chúng đã làm cùng một việc
Tuy nhiên mục tiêu cuối cùng có lẽ vẫn là các công cụ cấp trên nhận ra giá trị và trực tiếp cung cấp đầu ra có cấu trúc
Hay đấy
Tôi ủng hộ mạnh mẽ các công cụ CLI hỗ trợ đầu ra JSON hợp lý, và những thứ như https://github.com/WireGuard/wireguard-tools/blob/master/contrib/json/wg-json cùng
|ConvertTo-Jsoncủa PowerShell chiếm một phần lớn trong tự động hóa quản trị/giám sátTuy nhiên ở đây chữ hợp lý đang gánh rất nhiều việc, và thực tế vẫn là thực tế
Như cách thêm
Jsau lệnh của LSI/Broadcom StorCLI, hay các wrapper che COM của PowerShell: về mặt kỹ thuật thì là JSON, nhưng kết quả lại phức tạp hoặc vô dụng đến mức nực cười, khiến cuối cùng ta thường quay về giải pháp tạm bợ kiểu “cứ chạy regex lên đầu ra văn bản thuần thôi”Dù vậy tôi chắc chắn sẽ xem qua cái này
Nếu ví dụ đầu tiên, việc phân tích đầu ra
dig, đại diện cho những việc có thể làm một cách ổn định, thì có vẻ khá thú vịTôi nghĩ
jc dig example.comnên là cú pháp mặc địnhVì
dig example.com | jc --digbuộc phải đoán lại các flag và tham số của lệnh trước đó sau này để phân tích đầu raViệc mọi đầu ra đều là object là một trong những điểm tôi thích nhất ở PowerShell
Mỗi lần phải viết script bash là tôi lại nhớ điều đó
Xin bày tỏ sự kính trọng với người đã nhận duy trì thứ này
Nếu kiểu như
aws s3 ls | jc --aws=1.2.3thì đúng là ác mộngTôi nhớ đến chương trình
filetrên Linux hoặc Unix, và những anh hùng đang duy trì nóplugins, chẳng hạn để dưới dạng các lệnh shell riêng, thì có thể chuyển một phần công sức bảo trì sang cho tác giả pluginTôi thắc mắc liệu có ai biết danh sách các công cụ dòng lệnh Unix hiện đại nhận tùy chọn
--jsonkhôngCó thể cũng sẽ hữu ích nếu thêm thông tin như vậy vào repository này
ip, công cụ thay thế hiện đại choifconfig, có hỗ trợ JSONlldpctlcũng hỗ trợAnsible cung cấp chi tiết hệ thống dưới dạng JSON gọi là
facts, và được thiết kế để tự động hóa có thể tận dụng chúnglsblknhận flag--jsonvà có thể cung cấp rất nhiều thông tinCứ thử
lsblk --json --output-alllà đượcRất hữu ích khi script cần kiểm tra các đĩa và phân vùng của hệ thống
-T jsonDự án thú vị
Nhưng tôi cứ nghĩ họ sẽ dùng thứ như textfsm làm parser bước đầu
textfsm được dùng nhiều để parse đầu ra CLI của thiết bị mạng
https://github.com/google/textfsm