Ubuntu Weekly Recipe

第516回 command-not-found再発見

この記事を読むのに必要な時間:およそ 3.5 分

Ubuntuにはcommand-not-foundというパッケージが最初からインストールされています。ユーザーが入力したコマンドが存在しない場合,必要となりそうなパッケージを推測・提案してくれるニクイアイツです。今回はその仕組みとカスタマイズ方法を紹介しましょう。

command-not-foundの仕組み

UbuntuでCLIで操作している時,およそ人生においてコマンド入力を間違えたことのないパーフェクトヒューマンでもない限り,以下のようなメッセージに遭遇したことがあるでしょう。

プログラム 'sl' はまだインストールされていません。 次のように入力することでインストールできます:
sudo apt install sl

これはcommand-not-foundというパッケージが提供する機能です。Ubuntuのリポジトリには多種多様なソフトウェアが存在しますが,そのすべてがローカルシステムにインストールされているわけではありません。特に初期のUbuntuは「インストールイメージをCD一枚のサイズに収める」という制約を設けていたため,最初からインストールされているパッケージは限られていました。しかしながら実行しようとしているコマンドとそのパッケージ名が即座にわかるユーザーはそこまで多くありません。そんなユーザーのために,入力したコマンドが見つからなかったときに,可能性のあるパッケージをリストアップしてくれるのがcommand-not-found機能です。

command-not-foundはUbuntu 6.10ぐらいにパッケージが用意され,Ubuntu 7.04から標準でインストールされるようになった,Ubuntuの中ではとても歴史の古いツールです※1)⁠オリジナルの作者はCanonicalのZygmunt KrynickiMichael Vogtであり,その実体はPythonスクリプトとなっています※2)⁠

※1
過去には「notepad」と入力したら「Ubuntuには似たようなプログラムとしてgeditやkwriteがあります」とか,⁠excel」と入力したら「Ubuntuには(中略)gnumericがあります」と表示する機能もありました。
※2
ちなみにDebianにも同名のパッケージはありますが,基本的な考え方は同じもののいくつかの事情によりその仕組みは異なっています。具体的にはDebian版の場合,パッケージ名とコマンド名の対応表をapt-fileコマンドで生成します。また現時点ではPython3にも未対応のようです。

仕組みはすごく単純です。Bashには検索パスにコマンドが見つからない場合,command_not_found_handle関数を呼び出すという仕組みが存在します。Ubuntuのbashパッケージが提供する/etc/bash.bashrcには最初から,command-not-foundがインストール済みならcommand_not_found_handleを定義するスクリプトが組み込まれていますので,ユーザーが特に設定せずともcommand-not-foundを使えるようになっているのです※3)⁠

※3
zshにも同等のcommand_not_found_handler関数が存在します。command-not-foundパッケージによって/etc/zsh_command_not_foundがインストールされますので,zsh環境でも同じ機能が使えます。
# if the command-not-found package is installed, use it
if [ -x /usr/lib/command-not-found -o -x /usr/share/command-not-found/command-not-found ]; then
        function command_not_found_handle {
                # check because c-n-f could've been removed in the meantime
                if [ -x /usr/lib/command-not-found ]; then
                   /usr/lib/command-not-found -- "$1"
                   return $?
                elif [ -x /usr/share/command-not-found/command-not-found ]; then
                   /usr/share/command-not-found/command-not-found -- "$1"
                   return $?
                else
                   printf "%s: command not found\n" "$1" >&2
                   return 127
                fi
        }
fi

command-not-foundの実体は/usr/lib/command-not-foundです。ここにコマンド引数の最初の文字列(つまりはユーザーが入力したコマンドだと思われる文字列)を渡します。そして次のような流れでメッセージを表示します。

  • PATHにはないもののインストール済みかをチェックする
  • apt/aptitudeがインストールされていない場合は,ただ「見つかりません」のエラーのみを表示する
  • ブラックリスト(後述)に見つかったら,ただ「見つかりません」のエラーのみを表示する
  • パス名から該当するパッケージを検索する
  • パッケージが複数見つかったらパッケージリストとインストール方法を表示する
  • 該当するパッケージが1つのときはインストール方法sudo apt PACKAGEを提示する
  • 該当パッケージのコンポーネント(mainやuniverse)が無効化されていたら有効化するように伝える
  • パッケージが見つからずなおかつコマンドが3文字以上なら単なるスペルミスの可能性を考慮して「もしかして」を表示する

つまり「間違え方」によって結構表示の仕方が変わるのです。ちなみにadminグループに加入していたらsudoを使ったインストール方法が提示されますが,加入していない場合は次のようなメッセージになります。

プログラム 'sl' はまだインストールされていません。 'sl' を利用するために,コンピュータの管理者に 'sl' をインストールすることを相談してください

動作をカスタマイズする

command-not-foundは初心者にとって便利な機能ですが,ユーザーによっては「うざったい」と感じるツールでもあります。そこでいくつかのカスタマイズ方法も紹介しましょう。

command-not-foundを無効化する

無効化する一番手っ取り早い方法は,command-not-foundパッケージを削除することです。もしシステム全体としては残しておいて,特定のユーザーだけ無効化したいのであれば~/.bashrcに以下のスクリプトを追加しましょう。

# disable command-not-found
function command_not_found_handle {
    printf "%s: command not found\n" "$1" >&2
    return 127
}

要するに関数の定義を上書きしているだけですね。

著者プロフィール

柴田充也(しばたみつや)

Ubuntu Japanese Team Member株式会社 創夢所属。数年前にLaunchpad上でStellariumの翻訳をしたことがきっかけで,Ubuntuの翻訳にも関わるようになりました。

コメント

  • Re:

    unset command_not_found_handle でいいじゃん

    Commented : #1  /// (2018/04/18, 23:20)

コメントの記入