Help us understand the problem. What is going on with this article?

apk --updateという引数はない

More than 1 year has passed since last update.

はじめに

軽量な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を投げてみました。

https://github.com/gliderlabs/docker-alpine/pull/503

まとめ

  • apk --update という引数は apk --update-cache の間違いです。
  • getopt のGNU拡張 getopt_long はオプションが正確じゃなくても他に候補がないときには使えるため、--update でも --update-cache を指定したときと同じ動作になります。
  • インターネットの記事をコピペするだけじゃなくてちゃんと自分で調べましょう。

以上、自分への自戒でした。気をつけます。

mtgto
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした