- JC は、さまざまな CLI ツール、ファイル形式、一般的な文字列の出力を JSON に変換し、スクリプトでより簡単にパースできるよう支援するツール
- パイプ入力を受け取り、
STDOUT に JSON を出力し、jc dig example.com のようにコマンドの前に付ける magic syntax もサポート
- デフォルト出力はパーサーごとの 厳密なスキーマ を使用し、数値、
null、ブール値、追加の意味フィールドを変換し、-r または raw=True で前処理前の生 JSON にアクセス可能
- Python ライブラリとしても利用でき、JSON の代わりに Python の辞書、辞書のリスト、または 遅延 iterable 形式で結果を受け取れる
- 大規模・長時間実行のパイプライン向けに、JSON Lines/NDJSON を出力する ストリーミングパーサー、
--slurp、--meta-out、ローカルパーサープラグイン、プラットフォーム互換性警告の抑制オプションを提供
JC がすること
- JC(JSON Convert) は、さまざまな CLI ツール、ファイル形式、一般的な文字列の出力を JSON に変換する
- 変換された出力は
jq や jello のようなツールへパイプして追加処理できる
- 例として、
dig example.com | jc --dig で dig の出力を JSON 配列に変換し、その後 jq -r '.[].answer[].data' で IP アドレスだけを抽出する
- 同じ処理は
jc dig example.com | jq -r '.[].answer[].data' のように magic syntax でも実行可能
CLI と Python ライブラリの使い方
- 基本的な CLI の使い方は、パイプ入力を受け取って JSON 表現を出力する形式
COMMAND | jc [SLICE] [OPTIONS] PARSER
cat FILE | jc [SLICE] [OPTIONS] PARSER
echo STRING | jc [SLICE] [OPTIONS] PARSER
- magic syntax は
jc [SLICE] [OPTIONS] COMMAND または jc [SLICE] [OPTIONS] /proc/<path-to-procfile> 形式で、コマンドや /proc ファイルの前に jc を付ける
- コマンド alias と shell builtin はサポートしない
- Python では
jc.parse('dig', cmd_output) のように呼び出す
- 戻り値は JSON 文字列ではなく、Python の辞書、辞書のリスト、またはストリーミングパーサーの遅延 iterable になる場合がある
- Python パッケージのドキュメントは
help('jc')、help('jc.lib')、または オンラインドキュメント で確認できる
出力表現とスキーマ
- デフォルト表現はパーサーごとの 厳密なスキーマ を使用する
- 既知の数値は JSON の
int または float 値に変換される
- 既知の
None 値は JSON null に変換される
- 既知のブール値も変換される
- 一部のパーサーは追加の意味フィールドを付加する
- 生の前処理 JSON には、CLI の
-r オプションまたは Python ライブラリの parse(..., raw=True) でアクセスする
- 各パーサーのスキーマは、パーサー一覧の横にあるドキュメントリンクで確認できる
- JSON 出力はデフォルトで compact 形式で、
-p オプションで pretty format を使える
-y または --yaml-out オプションで YAML 出力も可能
対応パーサーと活用例
- 対応対象は CLI コマンド、ファイル形式、文字列パーサーで構成される
- README のパーサー一覧には
dig, ls, ping, ps, netstat, ifconfig, csv, xml, yaml, /etc/hosts, /etc/passwd, /proc/, systemctl, git log, jwt, url, semver, x509 関連ファイルなどが含まれる
- 出力例は、さまざまな入力を JSON 構造に変換する方法を示している
arp の出力は、アドレス、ハードウェアタイプ、MAC アドレス、インターフェースのフィールドに変換される
- CSV ファイルは、ヘッダーをキーにしたオブジェクト配列に変換される
/etc/hosts は、IP と hostname 配列に変換される
ifconfig は、インターフェース名、MTU、IPv4/IPv6 アドレス、パケット・バイトカウンターなどを含むオブジェクトに変換される
ping は、送受信パケット数、損失率、往復時間、応答リストを含むオブジェクトに変換される
- Ansible では
community.general コレクションの filter plugin として利用できる
インストール方法
jc は複数の方法でインストールできる
pip3 install jc
- OS パッケージリポジトリ
- GitHub Releases のアーキテクチャ別バイナリ
- OS パッケージのインストール例には以下が含まれる
- Debian/Ubuntu:
apt-get install jc
- Fedora:
dnf install jc
- openSUSE:
zypper install jc
- Arch:
pacman -S jc
- macOS:
brew install jc
- FreeBSD: ports 経由でインストール
- Ansible filter plugin:
ansible-galaxy collection install community.general
- FortiSOAR connector: FortiSOAR Connector Marketplace からインストール
主なオプション
-a / --about: jc とパーサー情報を JSON または YAML で出力する
-d / --debug: パース時に問題がある場合にトレースメッセージを出力し、-dd はさらに詳細なデバッグを提供する
-h / --help: ヘルプを表示し、jc -h --parser_name でパーサーのドキュメントを確認できる
-M / --meta-out: タイムスタンプ、パーサー名、magic command、magic command exit code などの メタデータ を出力に追加する
-q / --quiet: パーサーの警告メッセージを抑制し、-qq はストリーミングパーサーのエラーを無視する
-s / --slurp: 複数行入力を配列にまとめる
-u / --unbuffer: 出力バッファリングを無効化する
-B / --bash-comp, -Z / --zsh-comp: Bash または Zsh の completion script を生成する
Slice と Slurp
- Slice は Python の slicing に似た
START:STOP 構文で、入力行の一部だけを処理する
- たとえばヘッダーとフッターがある表では、
jc 1:-1 --asciitable または jc 1:4 --asciitable で中間データだけを JSON に変換できる
- 正の値と空の slice は最もメモリ効率がよく、負の slice はより多くのメモリを使用する
- Slurp は、単一行入力を受け取る文字列パーサーで複数項目を一度に配列として出力する機能
- 例: 1 行に 1 つずつ複数の IP アドレスが入ったファイルを
jc --slurp --ip-address で処理する
/proc magic syntax は、複数ファイル選択時に自動で slurp をサポートする
- 複数の
/proc ファイルを変換すると、各結果オブジェクトがどのファイルに対応するか分かるよう _file フィールドが追加される
--meta-out と slurp を併用すると、result 配列と _jc_meta オブジェクトで包まれた構造が出力される
終了コードとメタデータ
jc 内部の致命的なエラーは終了コード 100 を生成し、それ以外では 0 を返す
- magic syntax 使用時、
jc はパース対象プログラムの終了コードを保存し、jc の終了コードに加算する
ifconfig が 1、jc が 0 なら、結合終了コードは 1
ifconfig が 0、jc が 100 なら、結合終了コードは 100
- 両方がエラーなら、結合終了コードは
101
- magic syntax で
--meta-out または -M を使うと、_jc_meta オブジェクトに magic command 情報と終了コードが含まれる
色と環境変数
JC_COLORS 環境変数で key name、keyword、number、string の色を指定できる
- 形式は
JC_COLORS=<keyname_color>,<keyword_color>,<number_color>,<string_color>
- 色の値は
black, red, green, yellow, blue, magenta, cyan, gray, brightblack, brightred, brightgreen, brightyellow, brightblue, brightmagenta, brightcyan, white, default のいずれか
NO_COLOR 環境変数を設定すると色付き出力は無効化される
-C オプションは NO_COLOR 環境変数と -m オプションより優先され、色付き出力を強制する
ストリーミングパーサー
- ほとんどのパーサーは
STDIN 全体をメモリに読み込んでパースした後、JSON ドキュメントをシリアライズ出力する
- 一部の ストリーミングパーサー は、入力を受け取るとすぐに行単位で処理し、JSON Lines または NDJSON で出力する
- 例:
ls-s, ping-s
ls -lR / のような大きなコマンド出力でも、メモリ使用量を大幅に減らせ、場合によってはより高速に処理できる
- ストリーミングパーサーは magic syntax と併用できない
- 長時間実行パイプラインでパースエラーがパイプを壊さないようにするには、
-qq または Python の ignore_exceptions=True を使える
- 成功した行には
_jc_meta.success: true が含まれる
- 失敗した行には
_jc_meta.success: false、error、line フィールドが含まれる
- パイプ間で OS バッファの影響により出力の表示が遅い場合は、
-u で unbuffered output を使える
- ただし、大量データのストリームでは unbuffered output の方が遅くなることがある
- Python ではストリーミングパーサーは iterable 入力を受け取り、iterable オブジェクトを返して遅延処理できる
パーサープラグイン
- ローカル parser plugin はアプリデータディレクトリの
jc/jcparsers フォルダーに配置できる
- Linux/unix:
$HOME/.local/share/jc/jcparsers
- macOS:
$HOME/Library/Application Support/jc/jcparsers
- Windows:
$LOCALAPPDATA\\jc\\jc\\jcparsers
- プラグインは標準的な Python モジュールファイル
jc/parsers/foo.py または jc/parsers/foo_s.py をテンプレートとして使える
- プラグインのファイル名は有効な Python モジュール名でなければならず、文字で始まり、英数字と underscore のみを含められる
- ローカルプラグインはデフォルトパーサーを override できる
ロケール、タイムゾーン、互換性
- 最良の結果を得るには、
LC_ALL を C または en_US.UTF-8 に設定する方法が推奨される
- 一部の古いシステムでは
C locale が UTF-8 エンコーディングをサポートしておらず、UTF-8 出力が ASCII と \\u escape sequence に落ちることがある
- 一部のパーサーは計算された epoch timestamp フィールドを追加する
- フィールド名に
_utc suffix がなければ、ローカルタイムゾーン基準の naive timestamp とみなされる
- UTC タイムゾーンがコマンド出力テキストから検出されると timezone aware timestamp となり、キー名に
_utc suffix が付く
- UTC 以外のタイムゾーンは aware timestamp としてはサポートされない
- 一部のパーサーはプラットフォームごとの出力を変換するため、未対応プラットフォームで実行すると警告を表示する
- 未対応プラットフォームでも他システムの出力ファイルをパースでき、この場合
-q または Python の quiet=True で警告を抑制できる
- テスト済みプラットフォームには 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件のコメント
Hacker News のコメント
FreeBSD では、この問題は libxo によってある程度解決されている:
$ ps --libxo=json | jqのように構造化された出力を得られる完璧ではなく、
lsのサポートはあったものの何らかの理由で削除されており、すべてのユーティリティが対応しているわけでもないjcは多くのコマンドパーサーを提供する優れた暫定策に見えるが、そもそもパース用に設計されていないテキスト出力をパースするという限界があるユーティリティ群が共通フラグで構造化出力を出す方向にまとまるとよいし、PowerShell のようにデフォルトにするのは Unix/Linux にはやりすぎだとしても、標準の
--jsonフラグがあるだけで大きな前進になるhttps://wiki.freebsd.org/LibXo
https://reviews.freebsd.org/D13959
単に構造化データを渡すのではなく、不透明なオブジェクトをパイプラインに流し、前の段階に戻ってメソッドも呼び出せるものだと理解している: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_methods?view=powershell-7.4
jcやlibxoのようなラッパー、そして https://www.nushell.sh/ のような実験的なシェルも良いが、これらは実行可能なメソッドを持つオブジェクトというより、データの受け渡しに焦点を当てている構造化データは依然として Unix らしく感じられるし、本物のオブジェクトが必要なら Python や Ruby を起動すべき時だと思う
シェルがどれほど優れていてプログラミング機能が充実していても、いつシェルスクリプトから本格的なプログラミング言語へ移るべきかを知ることが重要だ
jcはこれまでこの役割を埋めるツールとして紹介されており、Unix ツール全般に-j/--json対応が広く普及するまでの橋渡しとして意図されているように読めるhttps://blog.kellybrazil.com/2019/11/26/bringing-the-unix-philosophy-to-the-21st-century/
/proc以下の項目が非構造化テキストファイルではなく JSON データを返すより構造的な解決策としては、ELF からデータ構造をエクスポートできるようにし、そのデータ構造を端末出力にシリアライズしたうえで、ユーザーが望む JSON や YAML などの形式で出力または処理できるようにする方法があり得る
正確なユーティリティ名は覚えていないが、おそらく
iostatで、JSON 出力行を文字列補間でフォーマットしていたところ、特定のフラグ組み合わせで完全に壊れた出力を生成していた今は改善されたのか分からないが、間隔オプションがある場合は JSON Lines のような形を期待していた
使い勝手の面では、PowerShell と kubectl は libxo よりはるかに先を行っていると思う
https://github.com/Juniper/libxo
https://libxo.readthedocs.io/en/latest/
アイデアは本当に良いが、保守を考えると不安になる
バージョンやコマンドフラグによる出力の変化などを考慮すると保守地獄のように見えるし、実際の利用は一部のケースではうまくいくだろうが、基本的な例を超えるとすぐに新鮮味が薄れそうだ
さらに、ツールのオプションに
--を使うのも良くなさそうに見える新しいパーサーごとに新しいフラグが必要なら、ヘルプやマニュアルページが何千行にもなり得る
あるユーティリティが独自の JSON エクスポートをサポートすることにしたら、その後はこのツールがその機能に委譲すればよい
jc lsのようにコマンドの前にjcを付けて使うこともできる--cmdパラメータは変換前にデータを加工できるようにするので、むしろ良いアイデアだたとえば、変換前に一覧を
grepしたいこともあるだろう保守の面でも、基本的な Unix コマンドの出力が大きく変わればこのツールだけでなく多数のスクリプトを壊すため、別のバイナリ更新のせいで思ったほど頻繁に壊れることはなさそうだ
よく管理された単一の共用パーサー集は、あちこちに散らばった場当たり的な出力パーサーより優れているが、複雑なことをしようとして直接 JSON 出力が必要になる状況では、パース自体が問題を増やさないでほしい
これを安心して使うには、結局、自分が使おうとするすべてのものについて追加のテストケースを PR として送ればよさそうだ
どうせそのテストは自分で書く必要があったはずだから
必要なツールのパース情報を人々が提出し、利用者が簡単に最新状態を保てるようにするしかない
Nushell はアプローチこそ異なるものの、ほぼ同じ地点である シェルコマンドの構造化データ に到達している
主にシェル自身がその役割を担う方式である
http://www.nushell.sh/
from json演算があり、JC と実際によく組み合う以前、Nushell のよい機能をいくつか紹介する動画を録画しており、19分あたりで
jcと組み合わせる話をしている: https://www.youtube.com/watch?v=KF5dtxVsn1E特定のパターンに合うディレクトリ内のファイルを調べ、更新時刻が互いに10分以内のものを削除するスクリプトを書こうとしていたとき、Nushell がこうした作業に向いていることを思い出した
少し触ってみたらついに感覚がつかめ、今ではすっかりはまっている
非構造化データであっても レコードのリスト のような形に変換して処理できれば強力である
ある意味では、どこにでもファイルがあるという点は素晴らしく、それが Unix の約束であり、Plan 9 ではさらに拡張された形になっている
しかし非構造化ファイルだったり、ファイルごとに形式がばらばらだったりすることも同じくらい足かせになる
awkのようなものだけで nginx のログファイル1つをパースしようとしても面倒になり得る大きな欠点の1つは、Linux ユーザー空間で大規模なシステムの書き換えや設計変更を実行するのが難しいことだ
もっと賢いシェルが欲しいし、ファイルも好きで、横には
awkの本も置いてあるが、そろそろ データのパース に本気で改善が必要な時期だと思うプログラムがカラー出力をレンダリングするかどうかを自分で判断するように、構造化された出力を出すかどうかもできるとよい
どんなテキスト UI でも API のように使えるため、人間が読みやすいテキストが優先される
そのためシェルスクリプティングは、サードパーティ製ブラウザ拡張を作るのに似ている
見て推測し、その推測をもとにパーサーを急ごしらえし、うまくいくことを願う、というやり方だ
機械が読むコンテンツ用の第3の標準出力があるとよい
ターミナルにはデフォルトで出力せず、パイプするときはこの出力がパイプされ、JSONL であり、man ページが契約を明記する方式だ
そうすれば標準出力は人間向けのままにでき、パースは可能だが脆いものだと理解したうえで行う作業になる
もちろん完全に後方互換性を壊す発想であり、非現実的に CLI を土台から再発明して現代化するなら、変えたいことのリストは長くなる
改善を得るのも本当に難しく、Lennart のような人が数十年ものの古い残骸を取り除こうとすると、すぐに騒ぎになる
JSON もまだ完全ではない
JSON はそこそこよいアイデアだが、きちんとやるにはデータ型のようなものを表現できる形式が必要だ
PowerShell により近い方式でなければ、数値を数値として扱い、
$a - $bで日付の差分を計算するという驚くべきことはできないGNU coreutils でこれが実現するとよい
問題は .NET とオブジェクトに深く根を下ろしすぎていて、どのプラットフォームでも既存のネイティブコマンドと統合するのが非常に難しい点だ
すでに本質的には同じことをしているからだ
ただ最終目標は結局、上位のツールが価値を認識して直接 構造化された出力 を提供することになりそうだ
すばらしい
妥当な JSON 出力をサポートする CLI ツールを強く支持しており、https://github.com/WireGuard/wireguard-tools/blob/master/contrib/json/wg-json のようなものや PowerShell の
|ConvertTo-Jsonは、管理・監視の自動化で大きな部分を占めているただしここで 妥当な が多くの仕事をしており、現実は現実だ
LSI/Broadcom StorCLI のコマンドの後ろに
Jを付ける方式や、PowerShell の COM 隠蔽ラッパーのように、技術的には JSON だが結果が途方もなく複雑だったり役に立たなかったりして、結局「平文出力に正規表現を少しかけよう」というその場しのぎに戻ることが多いそれでもこれはぜひ見てみるつもりだ
最初の例である
dig出力のパースが、安定してできることの代表例ならかなり興味深いjc dig example.comが基本構文であるべきだと思うdig example.com | jc --digでは、出力をパースするために前のコマンドのフラグやパラメータを後から推測しなければならないからだすべての出力がオブジェクトである点は、PowerShell で最も好きな部分の1つだ
bash スクリプトを書かなければならないたびに、それが恋しくなる
これをメンテナンスすることにした人に敬意を表する
aws s3 ls | jc --aws=1.2.3のような形なら悪夢だLinux または Unix の
fileプログラムと、それをメンテナンスする英雄たちを思い出すpluginsのようなものをロードできるなら、たとえば別のシェルコマンドの形にしておけば、メンテナンス作業の一部を プラグイン作者 に渡せる--jsonオプションを受け付けるモダンな Unix コマンドラインツールの一覧があるのか気になるこのリポジトリにそうした情報を追加しても有用かもしれない
ifconfigのモダンな代替ツールであるipは JSON をサポートしているlldpctlも対応しているAnsible はシステムの詳細情報を
factsという JSON として提供し、自動化がそれを活用するように設計されているlsblkは--jsonフラグを受け付け、多くの情報を出せるlsblk --json --output-allを試してみるとよいスクリプトがシステム上のディスクやパーティションを確認する必要があるときに非常に便利
-T jsonフラグで対応している興味深いプロジェクトだ
ただ、最初の段階のパーサーとして textfsm のようなものを使うと思っていた
textfsm はネットワーク機器の CLI 出力をパースするのによく使われている
https://github.com/google/textfsm