command-not-foundが使えない!!
インストールしてないコマンドを叩いた際に、どのパッケージをインストールすればいいかを表示してくれるcommand-not-found。
大変重宝していたのだけれど、ubuntu20.04にしてからちゃんと動かないのでどうしたものかな、、と放置していた。
流石に放置し続けもどうかなということで、原因と対策を調べてみた。
環境
docker ubuntu:20.04(latest, 2020.6時点)。
ちなみに後述するが、dockerのubuntu:18.04及びminimumインストールしたubuntu 20.04(server)では本事象は発生しない。
事象
これまで通りインストールしてないコマンドをコンソールで叩いてみると、、
Could not find command-not-found database. Run 'sudo apt update' to populate it.
てな具合に、command-not-foundをつかいたいなら、まずupdateせよと。
で、アップデートしてみると、、
# apt-get update
Get:1 http://security.ubuntu.com/ubuntu focal-security InRelease [107 kB]
Hit:2 http://archive.ubuntu.com/ubuntu focal InRelease
Get:3 http://archive.ubuntu.com/ubuntu focal-updates InRelease [107 kB]
Get:4 http://archive.ubuntu.com/ubuntu focal-backports InRelease [98.3 kB]
Fetched 312 kB in 2s (168 kB/s)
Traceback (most recent call last):
File "/usr/lib/cnf-update-db", line 26, in <module>
col.create(db)
File "/usr/lib/python3/dist-packages/CommandNotFound/db/creator.py", line 99, in create
self._fill_commands(con)
File "/usr/lib/python3/dist-packages/CommandNotFound/db/creator.py", line 133, in _fill_commands
self._parse_single_commands_file(con, fp)
File "/usr/lib/python3/dist-packages/CommandNotFound/db/creator.py", line 177, in _parse_single_commands_file
suite=tagf.section["suite"]
KeyError: 'suite'
Reading package lists... Done
E: Problem executing scripts APT::Update::Post-Invoke-Success 'if /usr/bin/test -w /var/lib/command-not-found/ -a -e /usr/lib/cnf-update-db; then /usr/lib/cnf-update-db > /dev/null; fi'
E: Sub-process returned an error code
(creator.pyは、内部の動作確認のために少し書き換えてるので、エラー行は本来とずれてます)
そもそもアップデートができない。。。
これはcommand-not-foundに限らずアップデート全般ができなくなるので、かなり由々しき問題なわけです。
なぜこうなった?
エラーを起こしている箇所は、エラーログのとおり、/usr/lib/python3/dist-packages/CommandNotFound/db/creator.py。
これはcommand-not-foundが、どのコマンドにはどのpkgが必要という情報を管理するDBを作成する際の処理で、apt-get update時に/usr/lib/cnf-updatedb経由で呼び出される。
内部をみていくと、apt_pkgというライブラリで/var/lib/apt/listsにある、apt情報を読み込んで、sqliteのDBを作ろうとしています。
この際、/var/lib/apt/lists配下のファイルはlz4で圧縮されてるのですけれど、これを解凍せずに読み込んでいるのでapt_pkgがデータを解釈できなくなってるのが原因、、、と思われる。
docker 18.04ではなぜ動く?
18.04も、creator.pyの処理や、lists配下のlz4圧縮といった条件は一緒っぽい。
ただし、command-not-foundのpkgを導入する際、合わせてcommand-not-foundの対応表のDB(/usr/share/command-not-found/commands.db)を配置してくれる。
これで、command-not-foundがちゃんと動くようである。
一方、creator.py(というか、それを呼び出している/usr/lib/cnf-update-db)を実行してみると、、
正常終了する、、、けど、実は何も仕事してない。
これは、内部の処理をよくよく見ると、/var/lib/apt/lists配下の全ファイルではなくCommandが名前にファイルだけを読み込むようにしているのだけど、apt-get時にCommandをダウンロードしてこないため、そもそも何も処理をしないから、ということっぽい。
仮想マシンにきちんとインストールした20.04では、、
ちゃんと動作する。
command.dbの場所が/var/lib/connamd-not-foundで少々ずれているが、まぁそれはいいとして。
また、/usr/lib/cnf-update-dbもちゃんと動く。
違いはというと、、、/var/lib/apt/lists配下のファイルがすべて生データで、lz4圧縮されてない!!
そもそも/var/lib/apt/listsて?
docker関連で調べてみたところ、コンテナ化の際ここは重いからまるごと削除してしまうものらしい。
ってことは、、、
あった。。。
dockerの場合、/etc/apt/apt.conf.d/docker-gzip-indexes
Acquire::GzipIndexes "false"; Acquire::CompressionTypes::Order:: "gz";
apt-get updateするなら持ってきてもいいけど、圧縮はするからね!という設定ですね。。
これを削除して、apt-get updateすると、無事lz4圧縮しないファイルが取得できました。
そしてcommand.dbも無事生成完了。
まとめ
command-not-foundを使うには、/var/lib/apt/lists配下の情報を圧縮せずに持ってこないといけない。
dockerはディスク膨張を嫌ってこのファイルは基本削除/持ってきても圧縮保存、としているので、その基本設定と干渉しちゃったのが問題っぽい。
ターミナル用途でdocker使うなよ、、、てことなんでしょうけれど、ターミナル用とで使いたい私からすると致し方なし。
まぁ、勉強にはなりました。
apt-fileでも良かったんですけど、そっちも同じような問題にぶつかったのかなぁ。。
気が向いたらまた調べて見るくらいで。