はじめに
軽量なDockerイメージを作るときに重宝されるAlpine Linuxのパッケージマネージャであるapk、そのapkでパッケージをインストールするときにつかう apk
コマンド、その引数に --update
というものがあると思っている人はいないでしょうか。何を隠そう私がその一人でした。
今日なんかいいオプションないかなと何気なく apk add --help
を見てたところ、あると思いこんでいた --update
がないことに気付きました。
ちなみに下記が執筆時点で最新のAlpine Linux 3.9での apk add --help
の実行結果です。
apk add --helpの出力結果 (クリックで開きます)
# apk add --help
apk-tools 2.10.1, compiled for x86_64.
usage: apk add [-h|--help] [-p|--root DIR] [-X|--repository REPO] [-q|--quiet] [-v|--verbose] [-i|--interactive] [-V|--version] [-f|--force] [--force-binary-stdout]
[--force-broken-world] [--force-non-repository] [--force-old-apk] [--force-overwrite] [--force-refresh] [-U|--update-cache] [--progress] [--progress-fd FD]
[--no-progress] [--purge] [--allow-untrusted] [--wait TIME] [--keys-dir KEYSDIR] [--repositories-file REPOFILE] [--no-network] [--no-cache] [--cache-dir CACHEDIR]
[--cache-max-age AGE] [--arch ARCH] [--print-arch] [-s|--simulate] [--clean-protected] [--overlay-from-stdin] [--no-scripts] [--no-commit-hooks]
[--initramfs-diskless-boot] [--initdb] [-u|--upgrade] [-t|--virtual NAME] PACKAGE...
Description:
Add PACKAGEs to 'world' and install (or upgrade) them, while ensuring that all dependencies are met
Global options:
-h, --help Show generic help or applet specific help
-p, --root DIR Install packages to DIR
-X, --repository REPO Use packages from REPO
-q, --quiet Print less information
-v, --verbose Print more information (can be doubled)
-i, --interactive Ask confirmation for certain operations
-V, --version Print program version and exit
-f, --force Enable selected --force-* (deprecated)
--force-binary-stdout Continue even if binary data is to be output
--force-broken-world Continue even if 'world' cannot be satisfied
--force-non-repository Continue even if packages may be lost on reboot
--force-old-apk Continue even if packages use unsupported features
--force-overwrite Overwrite files in other packages
--force-refresh Do not use cached files (local or from proxy)
-U, --update-cache Alias for --cache-max-age 1
--progress Show a progress bar
--progress-fd FD Write progress to fd
--no-progress Disable progress bar even for TTYs
--purge Delete also modified configuration files (pkg removal) and uninstalled packages from cache (cache clean)
--allow-untrusted Install packages with untrusted signature or no signature
--wait TIME Wait for TIME seconds to get an exclusive repository lock before failing
--keys-dir KEYSDIR Override directory of trusted keys
--repositories-file REPOFILE Override repositories file
--no-network Do not use network (cache is still used)
--no-cache Do not use any local cache path
--cache-dir CACHEDIR Override cache directory
--cache-max-age AGE Maximum AGE (in minutes) for index in cache before refresh
--arch ARCH Use architecture with --root
--print-arch Print default arch and exit
Commit options:
-s, --simulate Show what would be done without actually doing it
--clean-protected Do not create .apk-new files in configuration dirs
--overlay-from-stdin Read list of overlay files from stdin
--no-scripts Do not execute any scripts
--no-commit-hooks Skip pre/post hook scripts (but not other scripts)
--initramfs-diskless-boot Enables options for diskless initramfs boot (e.g. skip hooks)
Add options:
--initdb Initialize database
-u, --upgrade Prefer to upgrade package
-t, --virtual NAME Instead of adding all the packages to 'world', create a new virtual package with the listed dependencies and add that to 'world'; the actions of the
command are easily reverted by deleting the virtual package
This apk has coffee making abilities.
長いので update
でgrepしますと、
# apk add --help | grep update
[--force-refresh] [-U|--update-cache]
-U, --update-cache Alias for
そう、 --update
ではなく --update-cache
が正しい引数でした。
とはいえ "apk add --update" でぐぐるといくつも記事が出てくるので「これはいったいぜんたいどういうことなのだろう?」と思って調べてみました。
間違った引数は無視されているだけなのでは?
と思って存在しない引数を使ってみたんですが、
# apk add --doesnotexists
apk: unrecognized option: doesnotexists
とちゃんと警告されます。
つまり --update
はヘルプに載ってないにもかかわらず正しい引数として認識されているというわけです。
ソースコードを読む
apkのコードはgitリポジトリで公開されています。
このソースを読んでみたところ、getopt_longという関数で引数を処理していることがわかります。
この getopt_long (3)
という関数は man をみると
Long option names may be abbreviated if the abbreviation is unique or
is an exact match for some defined option.
(意訳)
--XXXX形式の長いオプションは他のオプションと重複しない限りであれば短縮可能である。
(完全に一致したオプションがあれば他のオプションの一部があっても一致したほうが選択される)
つまり、 apk --update
の --update
引数は、 --update-cache
の省略形としてgetopt_long
が認識していたから使用できるわけです。
これがどういうことかというと、apkが新たに --update-hogehoge
という引数を追加したら --update
はエラーになりますし、そもそもこの挙動自体 apk
の仕様というよりは getopt_long
の仕様で短縮された引数でも使えているだけなので引数の解析を別のライブラリに変更しただけで使えなくなるかもしれません。まあさすがにないだろうけど。
昔は --update
引数があったんじゃないの?
と思って --update-cache
より前に --update
がないかをGitリポジトリ内で調べてみました。
すると --update-cache
が追加されたのが2009年で、その時点で --update
はありませんでした。
--update-cacheが追加されたコミット (はじめて git bisect
つかった)
てことで、やっぱり apk
に --update
なんて引数はありませんでした。
となるとなんで apk add --update
という記述がインターネット上にいくつかあるのかが謎なんですが、最初に誰かが間違えたのが出回ってるんですかね・・・?
(追記) Docker hubの公式AlpineイメージのGitHubリポジトリのドキュメント に apk add --update nginx
という記述があることがわかりました。これが元凶なのかはわからないのですが、とにかくtypo修正のPull Requestを投げてみました。
まとめ
-
apk --update
という引数はapk --update-cache
の間違いです。 -
getopt
のGNU拡張getopt_long
はオプションが正確じゃなくても他に候補がないときには使えるため、--update
でも--update-cache
を指定したときと同じ動作になります。 - インターネットの記事をコピペするだけじゃなくてちゃんと自分で調べましょう。
以上、自分への自戒でした。気をつけます。