• Windows Subsystem for Linuxガイド 第43回 bash設定 シェルオプション編その1

bashには、シェルとしての動作を設定する「シェルオプション」と呼ばれる機能がある。シェルオプションは、キーワードを使い有効(on)または無効(off)のどちらかを指定する。

動作の確認には、WSL バージョン: 2.4.12.0(カーネル バージョン: 5.15.167.4-1)でUbuntu-24.04 LTSを使った。シェルは、GNU bash, version 5.2.21(1)-release (x86_64-pc-linux-gnu)を利用して検証を行った。また、オプションの情報に関しては、

Bash Reference Manual(英語)
https://www.gnu.org/software/bash/manual/html_node/index.html

を参考にした。bashのバージョンが異なると動作が異なることがあるため、手元のマシンでの検証時にはバージョンを確認されたい。

シェルオプションの設定

シェルオプションの現在の状態を得るには、bashの組み込みコマンドであるshoptを使う。何もオプションを指定せずにshoptコマンドを実行すると、現在のシェルオプションすべての状態を表示する。また、シェルオプション・キーワードを引数として指定すれば、該当のシェルオプションの設定状態が表示される。

シェルオプションを有効(on)にするには、“-s”オプションを、無効(off)にするには“-u”オプションをshoptコマンドの引数として指定する。

たとえば、“autocd”オプションを有効にするには、


shopt -s autocd

とする。

なお、シェルオプションはbashの起動時に起動オプションを使って指定することもできる(前回記事参照)。この場合、bashのコマンドラインに-oに続けてシェルオプション・キーワードを記述する。前述のautocdなら


bash -o autocd

とすることで、autocdオプションを有効にしてbashを起動できる。

シェルオプションのキーワード

(表01)は、bashのシェルオプションを分類したもの。分類は筆者が独断で行った。アルファベット順のキーワードならば、bashの組み込みコマンドのmanページ(man builtins)で見ることができる。

  • ■表01

ここでは、この分類に従ってオプションを解説する。

コマンドライン

コマンドラインに分類されるオプションは9つある(表02)。このうちわかりにくいのは「checkhash」、「lastpipe」、「varredir_close」だろう。

  • ■表02

「checkhash」とは、bash内部にあるコマンドの実行パスを記録したハッシュテーブルを、コマンド実行前に検証するかどうかを設定する。PATHに登録されたディレクトリにあるコマンド(実行ファイル)は、高速に起動するため、一回、起動したコマンドは、ハッシュテーブルに実行ファイルパスを記録する。同一コマンド2回目の実行からは、コマンドを探さずにハッシュテーブルの値を使って起動を行う。

しかし、2回目の実行の前に実行ファイルを別の場所に移動させると、ハッシュテーブルに記憶されているパスでは実行ファイルが見つからなくなり、エラーになる(写真01)。このオプションは、コマンドの実行前にハッシュテーブルに記録されているハッシュのパスが正しいかどうかをチェックする。

  • 写真01: bashはPATH内のコマンドは、起動したときのパスを内部のハッシュテーブルに記録し、2回目以降は、これを使って起動を行う。このため、実行ファイルが移動されると、ファイルが存在しないというエラーになってしまう。checkhashオプションを有効にすることで、起動前にハッシュに登録されているパスを調べ、ファイルがなければPATH内の検索を行う

bashがハッシュテーブルを使うのは、shの動作を引き継いだからだ。1970~1980年台のハードディスクの性能を考えると毎回、PATH内でコマンドを探すのは、かなりオーバーヘッドが大きく、ハッシュテーブルを使うことで高速化が行われていた。しかし、現在のSSDを使うシステムでは、よほど大量のパスを登録しない限り、検索は一瞬で終わる。このため、この設定はオンにしても、ほとんど速度的な足かせにはならないだろう。

「lastpipe」は、パイプの右側でreadコマンドなどを使って変数に代入するときの挙動を変えるものだ。パイプでつながれたコマンドは、サブシェルでの実行となるため、パイプの右側で読み込みが行われる変数は、サブシェルのものになるため、コマンドラインを実行しているシェルに戻ると変数が消えてしまう(写真02)。これを防ぐため、パイプラインの最後のコマンドに関しては、コマンドラインと同じシェルに処理を行わせるというのがこのオプションの役割だ。具体的には、以下のようなパイプラインでは、変数(myvar)が消えてしまう。

  • 写真02: lastpipeオプションは、パイプラインの最後のコマンドをコマンドラインと同じシェルで実行する。lastpipeオプションが無効だと、最後のコマンドはサブシェルで実行されるため、変数への代入が有効にならない(写真の1行目)。
    ただし、lastpipeオプションを正しく動作させるには監視モードをオフ(set +m)にする必要がある(写真の3行目)。なお、同等のことは、「$(……)」によるコマンド置換でも行うことができる。


echo "test" | sed s/t/x/ | read -d '' myvar1 ; echo $myvar1

しかし、lastpipeを使うことで、最後のreadコマンドをコマンドラインと同じシェルで実行できるようになり、変数が残る。


shopt -s lastpipe;echo "test" | sed s/t/x/ | read -d '' myvar2 ; echo $myvar2;shopt -u lastpipe

ただし、同じ代入処理はシェルオプションを使わず、コマンド置換を使っても実現可能だ。


myvar3=$(echo "test" | sed s/t/x/);echo $myvar3

このため、このlastpipeオプションを使う必要はないが、どうしてもパイプラインの最後で変数への読み込みを行いたい場合に使う。なお、このlastpipeを有効にしたものは、POSIX準拠の動作であり、そのために利用されることがある。

「varredir_close」は、リダイレクト指定の動作を変更するもの。bashのリダイレクトでは、シェル変数名を波括弧でくくった{varname}という表記をリダイレクト記号右側のファイルディスクリプタ指定に利用できる。このとき、bashは、シェル変数varnameに10以上の整数値を自動で割り当てる。このとき{varname}は、変数値を番号とするファイルディスクリプタとして扱われる。たとえば、


cmd {temp}>myfile

とするとシェル変数tempには10以上の整数値が入り、その値を持つファイルディスクリプタとして扱われる。もし代入値が10ならば、以下と同じになる


cmd 10>myfile

varredir_closeは、コマンドの終了時に{varname}で定義されたファイルディスクリプタを自動で閉じるオプションである。ただし、標準では、無効となっている。

コマンドラインに属するオプションのうち、設定しておくと良さそうなものは「autocd」、「checkhash」、「xpg_echo」の3つ。

「autocd」は、コマンド名がディレクトリ名と同じだった場合、これをcdコマンドの引数として実行する機能。コマンド名とディレクトリ名を混同しないか心配になるが、ディレクトリ名はフルパスまたは相対パス指定が必須なので、コマンド名と重なることはない。たとえば「/usr/bin」と指定すれば、「cd /usr/bin」が実行される。

ときどき、cdコマンドを入れ忘れて、ディレクトリ名だけをコマンドラインに入れてしまうことが多いようなら設定する価値がある。

「xpg_echo」は、組み込みコマンドのechoの引数でエスケープ文字の処理を有効にするもの(写真03)。通常は、echoコマンドに-eオプションを付けないと「\n」が改行として処理されない。時々-eを付け忘れて、再実行するようなら、設定しておく価値がある。

  • 写真03: echoコマンドの引数文字列にある逆スラッシュによるエスケープ文字は、-eオプションをつけないと解釈されない。しかし、xpg_echoオプションを有効にすると、-eオプションがなくてもエスケープ文字が解釈される

デバッグ

デバッグカテゴリのオプションは2つある。このうち「extdebug」は、bashのデバッグ機能(デバッガ-)を有効にするもの。

もう1つの「noexpand_translation」は、gettextによるスクリプト内メッセージの翻訳に関係する。シェルスクリプト内で「$”……”」と指定されたメッセージは、ローカライズの翻訳対象メッセージであることを示す。gettextによる翻訳後もダブルクオートで囲われたメッセージになる。しかし、「noexpand_translation」を有効にすると、翻訳後の文字列はシングルクオートで囲まれたものになる。ただし、翻訳が行われない場合には、ダブルクオートのままになる。なお、gettextによる翻訳は勝手に行われるわけではなく、開発者がロケールに応じた翻訳情報などを用意する必要がある。

次回は、残りのシェルオプションを解説する予定だ。

≫ Windows Subsystem for Linuxガイド 連載バックナンバー
https://news.mynavi.jp/tag/winsubsystem/