前書き
map aptで調べてもオプションについての説明がないし、私の見た限りどの記事を見ても根拠となる参考文献を示していないので、ひじょーーーーーーにもやもやしたので自分で読むことにした
だいぶ下手なまとめ方になったので、よっぽど暇な方以外は結論だけ読むこと推奨
ソースコードの入手
aptを使用してコマンドのソースコードを取得する
が、aptのオプションの話とは関係ない
コマンドのソースコードを入手したい人以外は読み飛ばし推奨
bashに慣れていない方向けの補足
以下実行するべきコマンドには先頭に\$を付ける、実際には\$を無視して実行すること
cd(カレントディレクトリ(現在作業を行っている場所)の移動)とls(カレントディレクトリのファイル内容を確認)とsudo(管理者権限で実行)とvim(文章編集用のコマンド)は知っている前提です
vimについては頑張って調べていただくか、コメントいただければ詳細を追加します
まずソースコードリポジトリを有効にする、sudo が必要
$ sudo vim /etc/apt/sources.list.d/ubuntu.sources
表示された内容のうち(以下は抜粋)
## See the sources.list(5) manual page for further settings.
Types: deb
URIs: http://archive.ubuntu.com/ubuntu/
のTypesに deb-srcを追加し保存する
## See the sources.list(5) manual page for further settings.
Types: deb deb-src
URIs: http://archive.ubuntu.com/ubuntu/
次にリポジトリ情報を更新する
$ sudo apt update
ソースコードはカレントディレクトリ直下にダウンロードされるのでディレクトリを作っておく、例(#!でひとつ前の引数を再利用している)
$ mkdir ~/apt_source
$ cd #!
apt sourceでほしいコマンドのソースを取得、sudo は必要ない
$ apt source apt
$ ls
apt-2.8.3 apt_2.8.3.dsc apt_2.8.3.tar.xz
$ cd pat-2.8.3
補足:/etc/apt/sources.listを編集しろという記事は古い
$ sudo vim /etc/apt/sources.list
>>>
# Ubuntu sources have moved to the /etc/apt/sources.list.d/ubuntu.sources
# file, which uses the deb822 format. Use deb822-formatted .sources files
# to manage package sources in the /etc/apt/sources.list.d/ directory.
# See the sources.list(5) manual page for details.
現在このファイルは使用されておらずこのような注意書きが残されているのみである
aptの中身を読む
今回はオプション-yの動きに絞ってコードを読み進めていくが、ほかのオプションについても同様に読み進めればよい
まずfindコマンドを間違える()
$ find -name "apt"
./completions/bash/apt
$ vim ./completions/bash/apt
ただ、いい感じのソースにたどり着けた(以下、コメントアウトには筆者の補足が含まれる)
# Debian apt(8) completion -*- shell-script -*-
_apt()
{
local sourcesdir="/etc/apt/sources.list.d"
local cur prev words cword
_init_completion || return
## GENERIC_APT_GET_OPTIONSにはいくつかのグループのコマンドに対して使用できる
##オプションを変数にまとめてまとめてある
local GENERIC_APT_GET_OPTIONS='
-d --download-only
-y --assume-yes
## 中略
--no-install-suggests
--fix-policy
'
## コマンド(installやsearchなど)を
# see if the user selected a command already
local COMMANDS=(
"list"
"search"
## 中略
"depends" "rdepends"
"policy")
## 中略
## COMPREPLYは補完機能を設定するためのbash組み込みコマンド、つまり度のコマンドがどのオプションを持つか予測できる
if [[ $cur == -* || ( -v command && $cword -le $i ) ]]; then
case ${command-} in
install|reinstall|remove|purge|upgrade|dist-upgrade|full-upgrade|autoremove|autopurge)
COMPREPLY=( $( compgen -W '--show-progress
--fix-broken --purge --verbose-versions --auto-remove
-s --simulate --dry-run
--download
--fix-missing
--fix-policy
--ignore-hold
--force-yes
--trivial-only
--reinstall --solver
-t --target-release'"$GENERIC_APT_GET_OPTIONS" -- "$cur" ) )
return 0
;;
## 中略
complete -F _apt apt
# ex: ts=4 sw=4 et filetype=sh
どうやらaptコマンドをbashに打ち込んでいるときの、補完機能のためのシェルスクリプトのようだ。ここに書かれている短縮形とフルスペルの対応を見比べればある程度オプションの意味は取れるだろう。また全部でどれほどのオプションがあるのかということもほぼ正確に把握できる。
私が知りたかった-yオプションの意味も、yes,noで答える質問に対して予めyesを選択しておけるもの、ということにおおよそ確証が持てた。
次にコマンド実体のソースコードを探しに行く
$ find -name "*apt.*"
./cmdline/apt.cc
./debian/apt.apt-compat.cron.daily
## 以下略
.cを探していたので.ccってなんだと思ったら、C++の拡張子だった(cppしか知らんかった)ので見に行く
//(略)
static std::vector<aptDispatchWithHelp> GetCommands() /*{{{*/
{
// advanced commands are left undocumented on purpose
return {
// query
{"list", &DoList, _("list packages based on package names")},
{"search", &DoSearch, _("search in package descriptions")},
{"show", &ShowPackage, _("show package details")},
// package stuff
{"install", &DoInstall, _("install packages")},
//(中略)
{"changelog", &DoChangelog, nullptr},
{"info", &ShowPackage, nullptr},
{nullptr, nullptr, nullptr}
};
}
int main(int argc, const char *argv[]) /*{{{*/
{
CommandLine CmdL;
auto const Cmds = ParseCommandLine(CmdL, APT_CMD::APT, &_config, &_system, argc, argv, &ShowHelp, &GetCommands);
int const quiet = _config->FindI("quiet", 0);
if (quiet == 2)
{
_config->CndSet("quiet::NoProgress", true);
_config->Set("quiet", 1);
}
InitSignals();
InitOutput();
CheckIfCalledByScript(argc, argv);
CheckIfSimulateMode(CmdL);
return DispatchCommandLine(CmdL, Cmds);
}
これを見る限り、searchやinstallなど第1引数のコマンドごとにソースが存在していて、apt本体自体はそれぞれを参照するだけ(いくつかのチェックは存在するが)だと思われる。
また
$ grep -r DispatchCommandLine
apt-private/private-cmndline.cc:unsigned short DispatchCommandLine(CommandLine &CmdL, std::vector<CommandLine::Dispatch> const &Cmds) /*{{{*/
apt-private/private-cmndline.h:APT_PUBLIC unsigned short DispatchCommandLine(CommandLine &CmdL, std::vector<CommandLine::Dispatch> const &Cmds);
## (以下略)
より、メイン関数で返されているDispatchCommandLineはaptが独自にわざわざ用意した関数らしい。おそらく上で定義されているGetCommands以外はさらに別なファイルで独自定義されている関数がほとんどだろう。
一旦コマンドライン引数の流れを確認するためDispatchCommandLineのあるapt-private/private-cmndline.ccを確認する
このうち'y'を含む行を調べると
static bool addArgumentsAPTGet(std::vector<CommandLine::Args> &Args, char const * const Cmd)/*{{{*/
// 略
addArg('y',"yes","APT::Get::Assume-Yes",0);
addArg('y',"assume-yes","APT::Get::Assume-Yes",0);
addArg(0,"assume-no","APT::Get::Assume-No",0);
// 略
これは引数の&Argsに要素を追加していく関数である(詳説はここ)
AddArgは同じファイルの
#define addArg(w, x, y, z) Args.emplace_back(CommandLine::MakeArgs(w, x, y, z))
にプリプロセッサとして定義される、Argsはこのコードのいたるところで関数の引数の名前としていたるところに使用されているのでこのようなプリプロセッサが便利である。emplace_backはvector要素の末尾に要素を追加する。
https://cpprefjp.github.io/reference/vector/vector/emplace_back.html
CommandLine::MakeArgsは(同じくgrepで調べると)apt-pkg/contrib/cmndline.ccにあることがわかる
CommandLine::Args CommandLine::MakeArgs(char ShortOpt, char const *LongOpt, char const *ConfName, unsigned long Flags)/*{{{*/
{
/* In theory, this should be a constructor for CommandLine::Args instead,
but this breaks compatibility as gcc thinks this is a c++11 initializer_list */
CommandLine::Args arg;
arg.ShortOpt = ShortOpt;
arg.LongOpt = LongOpt;
arg.ConfName = ConfName;
arg.Flags = Flags;
return arg;
}
CommandLine::Argsは同じファイル……のヘッダファイルに
struct CommandLine::Args
{
char ShortOpt;
const char *LongOpt;
const char *ConfName;
unsigned long Flags;
inline bool end() {return ShortOpt == 0 && LongOpt == 0;};
inline bool IsBoolean() {return Flags == 0 || (Flags & (Boolean|InvBoolean)) != 0;};
};
と定義されている、つまりただただ構造体を返すだけですね
わざわざCommandLine名前空間を明示してるのは衝突しやすい名前を使っているせいかな
addArgumentsAPTGetのretrunはおそらくaptの第一引数のコマンドになにかオプションを追加するものだった時にtrueを返すもので、今回は大筋に影響しない
この関数はgetCommandArgsで呼び出され……とこの時の私は辿って行ったが、実はAPT::Get::Assume-Yesがaptのオプションを辿るうえで一番重要である。
……実はダウンロードしたファイルの中にドキュメントが存在していた。パスはdoc\po\ja.poである。このファイルでAPT::Get::Assume-Yesを調べると
"プロンプトへの自動承諾 - すべてのプロンプトに自動的に \"yes\" と答え、非対話"
"的に実行します。保留したパッケージの状態を変更したり、必須パッケージを削除す"
"るような不適切な状況の場合、<literal>apt-get</literal> は処理を中断します。設"
"定項目: <literal>APT::Get::Assume-Yes</literal>"
との記述があり、APT::Get::Assume-Yes、これに対応するオプション-yの機能についてようやく確認が取れたことになる
なら最初からオプション名で調べればいいじゃんと思うかもしれないが、ショートオプションの-y事態の説明はこのドキュメントになく、-yに類似の--assume-noオプションは説明がない。しかしAPT::Get::Assume-YesやAPT::Get::Assume-Noは明確に説明があるので、APT::XXX::YYYの形でドキュメントを参照するのが最も効果的と思われる。
また、ドキュメントに書いてありました、だとつまらなすぎるのでコードもさらに読み進める。APT::Get::Assume-Yesが含まれるファイルにapt-private/private-output.ccがある。該当箇所は
// YnPrompt - Yes No Prompt. /*{{{*/
// ---------------------------------------------------------------------
/* Returns true on a Yes.*/
bool YnPrompt(char const * const Question, bool const Default, bool const ShowGlobalErrors, std::ostream &c1o, std::ostream &c2o)
{
auto const AssumeYes = _config->FindB("APT::Get::Assume-Yes",false);
//中略
if (AssumeYes)
{
// TRANSLATOR: "Yes" answer printed for a yes/no question if --assume-yes is set
c1o << _("Y") << std::endl;
return true;
}
//中略
}
コードの内容と、この関数がcmdline/apt.ccに記述のあったDoInstall関数からも呼び出されていることから、APT::Get::Assume-Yesが有効な時に、標準出力に'Y'を流していること予想ができる。
これでコードからもオプション-yの挙動の予想を付けることができたことにしてください……
結論
aptオプションの挙動を知りたければまずソースコードを入手する
入手したファイルの中にドキュメントがある(doc/po/ja.po)
ただし、オプションについての説明はないので、apt-private/private-cmndline.ccから知りたいオプションに対応する設定項目、つまりオプション-yであれば
addArg('y',"assume-yes","APT::Get::Assume-Yes",0);
のうちのAPT::Get::Assume-Yes、を探し出しドキュメントで参照すればオプションの挙動が把握できるであろう
また、どんなオプションがあるのか知りたい方は、同じくapt-private/private-cmndline.cc内のaddArgumentsAPTGet関数や、その類似の関数を見た上で同じようにドキュメントを参照するとよいだろう
後書きと感想
ご指摘、ご質問等あれば遠慮なくコメントやDMをいただけると嬉しいです
私が当初予想してたソース(bash組み込みコマンド程度のソース)より構造が非常に複雑で、後半雑になってしまったのは非常に申し訳ないです
また実際にコマンドライン引数を処理している部分がよくわからなかったのも若干気持ちが悪い…(予想はつくが記事にできるほど正確に読み取れなかった、たぶんConfigurationクラスを_configという名前でインスタンス化してそこにargvを処理したフラグを設定している)
けど久しぶりに他人のコードが読めて満足
また何かの役に立ったり、ソースが見つからなくてもやもやしていた気分の解消につながったならいいねしていただけると、私が非常に喜ぶとともに新しい記事のモチベーションになります!