Ubuntu で Main リポジトリ以外のインストール済みパッケージを一覧にする

以下のコマンドを使用すると、 Ubuntu にインストールされているパッケージのうち、Main リポジトリ以外(Restricted, Universe, Multiverse)に属するものを一覧で表示できる。

dpkg-query --show --showformat='${binary:Package}\n' | xargs -IX bash -c "apt-cache policy X | sed -n --regexp-extended '1h;/.*(\/restricted|\/universe|\/multiverse) .*/{x;p;x;p;Q}'"

以上!

…とすると、この記事だけでは少し物足りない気がするので、使い方についても少し説明しておく。

なぜ Main リポジトリ以外のものを把握したいのか?

パッケージの取得元リポジトリのカテゴリを把握する理由は、Canonical による Ubuntu Pro のサポート範囲が関係しているからだ。

apt コマンドを使用して、Ubuntu の標準リポジトリから取得される各パッケージは、以下の4つのカテゴリに分類される。 1

  • Main
    • Ubuntu チームによってサポートされる、フリーソフトウェアを収容するコンポーネントで、 2,300 以上のパッケージを含む。
    • Ubuntu インストール時にデフォルトでインストールされるパッケージの大半はこれ。
  • Restricted
    • ドライバーなど、利便性の面でデフォルトでインストールされたほうが望ましいものの、プロプライエタリであるソフトウェアを含むコンポーネント。
  • Universe
    • Ubuntu コミュニティによって管理されている、 Linux 界の多くのオープンソースソフトウェアを収容するコンポーネントで、 23,000 以上のパッケージを含む。
  • Multiverse
    • フリーソフトの要件を満たさないような、特殊なライセンス要件をもつソフトウェアを収容するコンポーネント。
    • GPU 関連 (CUDA 等) や, VirtualBox などが含まれている。

これらのうち、Restricted と Multiverse リポジトリのパッケージについては、それぞれのソフトの提供元によってのみメンテナンスされ、Canonical によるサポートは行われない。

一方で、Main と Universe リポジトリのパッケージについては、セキュリティ修正の提供期間や、電話/オンラインチケットサポート範囲が、 Ubuntu Pro の有無やライセンスの種類ごとに以下のような内訳となっている。 2

Security patching Ubuntu LTS Ubuntu Pro (Infra-only) Ubuntu Pro
Over 2,300 packages in Ubuntu Main repository 5 years 10 years 10 years
Over 23,000 packages in Ubuntu Universe repository Best effort Best effort 10 years
Optional phone/ticket support No Yes Yes

5年のLTSの期間中、Main リポジトリのパッケージを主に使用する場合、(ライブパッチ等の他のUbuntu Proのサービスが不要であれば)無料の範囲内で十分だ。

Main リポジトリのパッケージを主に使用しながら、10年の延長サポートを望む場合は、"Ubuntu Pro (Infra-only)" を契約することをおすすめする。

また、Universe リポジトリのパッケージを積極的に活用したい場合は、"Ubuntu Pro" を契約しておくと、より安心だろう。

Ubuntu の Universe リポジトリのパッケージの更新基準

続きを読む

Docker run の起動時に任意コードを実行後 bash や ash を終了しない

この記事は、 Docker Advent Calendar 2022 の 23日目の記事だ。
空いていたので埋めちゃうよ。

この記事では、 bash や ash で任意のコードの実行後、ターミナルを終了せずに入力待ちにする方法について紹介する。
特に、 docker run の実行後に、その環境を維持したまま入力待ちにすることを考える。

例えば、 Windows コマンドプロンプトや PowerShell であれば、 CMD /K *** オプションや、 -NoExit -Command *** オプションで実現できるような内容だ。

本来なら、 docker build にてその任意コードの実行後の内容をイメージにするべきだが、 わざわざ build するまでもないとか、 build できない事情などもあるかもしれない。

ということで、 bash の場合と、 alpine などで使われる BusyBox ash それぞれについて、 docker run 実行時に任意コード実行後、ターミナルの入力待ちにする方法を紹介する。

bash の場合

bash の場合、 --rcfile オプションにて、起動時に実行するコマンドを指定できる。

ただし、 --rcfile はファイルを指定する必要があるため、 替わりにプロセス置換で実行コードを与えてやる方法をとる。

user@hostmachine:~$ docker run --rm -it debian:bullseye bash -c "bash --rcfile <(echo 'ls && export '\''FOO=B A R'\'' && MY_TIME=\$(date)')"
bin   dev  home  lib64  mnt  proc  run   srv  tmp  var
boot  etc  lib   media  opt  root  sbin  sys  usr
root@container:/# echo $FOO : $MY_TIME
B A R : Thu Dec 22 15:00:00 UTC 2022
root@container:/# 

プロセス置換はコンテナ内で実行される必要があるため、一旦 bash -c にてコンテナ内で bash 実行させ、その中で改めて --rcfile オプションを指定した bash を起動する流れとなる。

実際に実行したいコマンドは、 echo で文字列として書き出す。
上記例では、 ls && export 'FOO=B A R' && MY_TIME=$(date) と言う文字列を echo させている。

引用符が二重三重になっていて、エスケープが非常に難しくなっているので注意。

BusyBox ash

alpine 3.15 以降に含まれる BusyBox の ash であれば、意外にもプロセス置換が使える。

しかし、 --rcfile に相当するオプションは残念ながら無い。
替わりに、 ash には ENV という環境変数に記載されたファイルを ash 起動時に実行する機能がある。

これを使おう。

user@hostmachine:~$ docker run --rm -it alpine:3.15 ash -c "ash -c 'export ENV=\$1;ash' -s <(echo 'ls && export '\''FOO=B A R'\'' && MY_TIME=\$(date)')"
bin    etc    lib    mnt    proc   run    srv    tmp    var
dev    home   media  opt    root   sbin   sys    usr
/ # echo $FOO : $MY_TIME
B A R : Thu Dec 22 15:00:00 UTC 2022
/ # 

ash-c のコマンドに対して引数を与える -s オプションを使ってプロセス置換のファイルを与え、 それを $1 経由で ENV 環境変数にセット。 その状態で再度 ash を起動させれば、 bash と同様のことが行える。

ENV 環境変数に直接プロセス置換のファイルを指定せず、わざわざ一旦引数を経由させているのは、 入力を受け付ける ash プロセスが動いている間、 プロセス置換のファイルにアクセス可能にする必要があるためだ。
例えば、 export ENV=<(echo 'command');ash と実行しても、 ash 実行の段階ではプロセス置換のファイルが閉じられているので、コマンドは実行されない。

もうちょっと複雑で実用的な例

エスケープが何重にもなっていてややこしいが、書き方さえ気をつければ基本的にどんな内容でも実行可能だ。

apt パッケージマネージャーのリポジトリを書き換えた状態で、 bash を起動する方法 (Ubuntu <= 22.04):

docker run --rm -it ubuntu:22.04 bash -c "bash --rcfile <(echo 'sed -i -E '\''s%^(deb(-src|)\s+)https?://archive\.ubuntu\.com/ubuntu/%\1http://ftp.udx.icscoe.jp/Linux/ubuntu/%'\'' /etc/apt/sources.list && apt update && FooBar=`date -uIs`')"

〃 (Ubuntu 24.04):

docker run --rm -it ubuntu:24.04 bash -c "bash --rcfile <(echo 'sed -i -E '\''s%^(URIs:\s+)https?://archive\.ubuntu\.com/ubuntu%\1http://ftp.udx.icscoe.jp/Linux/ubuntu%'\'' /etc/apt/sources.list.d/ubuntu.sources && apt update && FooBar=`date -uIs`')"

apk パッケージマネージャーのリポジトリを書き換えた状態で、 ash を起動する方法 (Alpine):

docker run --rm -it alpine:3.15 ash -c "ash -c 'export ENV=\$1;ash' -s <(echo 'sed -i -E '\''s%^https?://dl-cdn\.alpinelinux\.org/alpine/%http://ftp.udx.icscoe.jp/Linux/alpine/%'\'' /etc/apk/repositories && apk update && FooBar=`date -uIs`')"

参考: https://stackoverflow.com/questions/74094552/how-not-to-terminate-after-carried-out-commands-in-bash

bash で標準出力と標準エラーを 2つのコマンドに出し分ける

本記事は シェルスクリプトのカレンダー | Advent Calendar 2021 - Qiita 17日目の記事だ。

殆どカレンダーが埋まってなかったので、思いついたネタで埋めちゃえ埋めちゃえ。

今回は、 bash 系列 (bash, zsh 等) の プロセス置換 (process substitution) 機能の話だ。

このプロセス置換は POSIX 互換の機能では無いため、以降の例は ash 系列 (busybox hush (ash), dash 等) では利用できない。

bash のプロセス置換

続きを読む

置換ができない/複数ある場合に sed の終了コード0以外にする

本記事は、 シェルスクリプト Advent Calendar 2021 の 4日目 の記事だ。
そして、 且つ docker Advent Calendar 2021 4日目 の記事でもある。

どちらのカレンダーもまだまだスッカスカなので、禁じ手で埋めにかかってしまった。


Docker 公式イメージ などをベースにして、カスタムしてイメージをビルドして使おうとした際、 なるべくなら /etc/apt/apt.conf.d/ 等のように、設定用のファイルを追加して、ツール側がいい感じにマージして利用してくれるのが望ましい。
しかし、 場合によってはやむを得ず、既存のファイルを sed コマンドなどで編集せざるを得ないこともあるだろう。

カスタムイメージの Dockerfile をビルドする際に、当初は意図通り書き換えられていても、イメージが更新された結果、イメージのリビルド時にファイルの書き換えが意図しない結果となってしまう場合がある。 1

通常、 sed コマンドは、置換が発生してもしなくても、 終了コード 0 で終了する。
このため、書き換えの成否にかかわらず、 docker build 時にエラーにならないため、コンテナ実行時に初めて置換が意図しない結果だったことに気づくことがある。

そこで、sed コマンドの書き換えで適切なパターンが見つからなかった場合に 0以外の終了コードを返し、ビルド時にエラーとする方法を考える。

続きを読む