FreeBSD Advent Calendar 2021 2日目の記事です。
今日はFreeBSD-11からサポートされている、LibXoの紹介をしようと思います。
LibXoとは?
LibXoはFreeBSD-11以降でサポートされた、任意のデータをテキスト、XML、JSON、HTMLの各フォーマットで出力するライブラリです。アプリケーション側がLibXoでの出力に対応していれば、ユーザ側は自分の扱いやすいフォーマットでデータを処理することが可能になります。
ドキュメントやソースコードは以下のURLで公開されています。GitHubリポジトリを見ると、どうやらJuniperによりメンテナンスされているライブラリのようですね。
- LibXo - A Library for Generating Text, XML, JSON, and HTML Output(FreeBSD Wiki)
- libxo(3)
- https://github.com/Juniper/libxo
LibXoが組み込まれているコマンド
wcコマンドでの例
FreeBSDでは、ベースコマンドのいくつかにLibXoが組み込まれています。例えば、wc(1)では以下のような形でコマンドの出力を柔軟に変換できます。
$ cat /etc/os-release
NAME=FreeBSD
VERSION=13.0-RELEASE
VERSION_ID=13.0
ID=freebsd
ANSI_COLOR="0;31"
PRETTY_NAME="FreeBSD 13.0-RELEASE"
CPE_NAME=cpe:/o:freebsd:freebsd:13.0
HOME_URL=https://FreeBSD.org/
BUG_REPORT_URL=https://bugs.FreeBSD.org/
$
$ cat /etc/os-release | wc
9 10 222
wc
コマンドに --libxo json,pretty
を指定するとJSONフォーマット、 --libxo xml,pretty
でXMLフォーマットでの出力になります。
$ cat /etc/os-release | wc --libxo json,pretty
{
"wc": {
"file": [
{
"lines": 9,
"words": 10,
"characters": 222
}
]
}
}
$
$ cat /etc/os-release | wc --libxo xml,pretty
<wc>
<file>
<lines>9</lines>
<words>10</words>
<characters>222</characters>
</file>
</wc>
libxoが組み込まれているコマンド
ldd wc
で参照しているライブラリを確認してみると、 /lib/libxo.so.0
を参照していることが分かります。おそらくこのライブラリを参照しているコマンドは --libxo json,pretty
のような指定が可能になっているのでしょう。
$ ldd `which wc`
/usr/bin/wc:
libxo.so.0 => /lib/libxo.so.0 (0x20444000)
libcasper.so.1 => /lib/libcasper.so.1 (0x20462000)
libcap_fileargs.so.1 => /lib/casper/libcap_fileargs.so.1 (0x2046a000)
libc.so.7 => /lib/libc.so.7 (0x20470000)
libutil.so.9 => /lib/libutil.so.9 (0x20640000)
libnv.so.0 => /lib/libnv.so.0 (0x20656000)
簡単なスクリプトで /lib/libxo.so.0
を参照しているコマンドを抽出してみます。
for i in `echo $PATH | sed -e "s/:/ /g"`
do
for j in `find $i -type f`
do
echo "===> $j"
ldd $j
done
done \
| egrep '^===>|libxo' \
| grep -B1 libxo \
| grep '^===>'
以下のコマンドが該当するようです。
===> /bin/df
===> /bin/ps
===> /sbin/savecore
===> /usr/bin/netstat
===> /usr/bin/w
===> /usr/bin/penv
===> /usr/bin/last
===> /usr/bin/xo
===> /usr/bin/pwdx
===> /usr/bin/pargs
===> /usr/bin/uptime
===> /usr/bin/iscsictl
===> /usr/bin/nfsstat
===> /usr/bin/vmstat
===> /usr/bin/procstat
===> /usr/bin/wc
===> /usr/sbin/lastlogin
===> /usr/sbin/sesutil
===> /usr/sbin/jls
===> /usr/sbin/arp
psコマンドでのサンプル
/lib/libxo.so.0
を参照しているコマンドには、 ps
コマンドもあり、より実用的な利用例を示せそうです。
(個人的には jls
でも --libxo json,pretty
が利用できるのは嬉しい感じです)
$ ps ax --libxo json,pretty
{
"process-information": {
"process": [
{
"pid": "2467",
"terminal-name": "- ",
"state": "IsJ",
"cpu-time": "0:08.29",
"command": "/usr/sbin/syslogd -s"
},
{
"pid": "2549",
"terminal-name": "- ",
"state": "IsJ",
"cpu-time": "0:00.08",
"command": "/usr/sbin/sshd"
},
...
ps ax --libxo json,pretty
での出力を jq
コマンド+JSONPathで欲しい項目のみ抽出することが可能になります。
(少なくともシェルスクリプトで細々と文字列処理するよりは楽な感じですね)
$ ps ax --libxo json,pretty | jq -r '."process-information".process[] | select(.command | startswith("vim"))'
{
"pid": "48072",
"terminal-name": "12 ",
"state": "TJ",
"cpu-time": "0:00.10",
"command": "vim -R wc.c"
}
$
$ ps ax --libxo json,pretty | jq -r '."process-information".process[] | select(.command | startswith("vim")) | .pid, .command'
48072
vim -R wc.c
まとめ
FreeBSD-11からサポートされた、LibXoについて紹介しました。
ベースコマンドのいくつかはLibXoを組み込んだ状態で提供されており、 --libxo json,pretty
のような指定でJSON,XML等の任意の形式で出力を得られます。JSONやXMLは jq
や xmllint
等でのパースや条件にマッチする項目の抽出が柔軟に行えるため、シェルスクリプトにおける可読性やメンテナンス性の向上に役立ちそうです。