1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

direnvのコマンド direnv hook bash で出力されるスクリプトについて調べてみた

Last updated at Posted at 2024-11-21

以前、以下記事でdirenvのインストール手順 hook direnv into your shell. について調べてみました。

その際、direnvのhookコマンドで出力されるBash用のフックを設定するためのスクリプトが気になったため、少し調べてみました。

direnv hook bash でどんなスクリプトが出力されるの?

Macのターミナルでdirenv hook bashを実行し、スクリプトを出力してみました。

% direnv hook bash

_direnv_hook() {
  local previous_exit_status=$?;
  trap -- '' SIGINT;
  eval "$("/opt/homebrew/bin/direnv" export bash)";
  trap - SIGINT;
  return $previous_exit_status;
};
if [[ ";${PROMPT_COMMAND[*]:-};" != *";_direnv_hook;"* ]]; then
  if [[ "$(declare -p PROMPT_COMMAND 2>&1)" == "declare -a"* ]]; then
    PROMPT_COMMAND=(_direnv_hook "${PROMPT_COMMAND[@]}")
  else
    PROMPT_COMMAND="_direnv_hook${PROMPT_COMMAND:+;$PROMPT_COMMAND}"
  fi
fi

いくつかわからない処理があったので、以下の要素に分解して調べてみましょう。

  • local
  • trap
  • PROMPT_COMMAND

localとは

出力されたスクリプトの_direnv_hook()内で使われており、Bashのマニュアル4.2 Bash Builtin Commandsに記載があります。

_direnv_hook()
_direnv_hook() {
  local previous_exit_status=$?;
  〜省略〜
  return $previous_exit_status;
};

local [option] name[=value] …
For each argument, a local variable named name is created, and assigned value. The option can be any of the options accepted by declare. local can only be used within a function; it makes the variable name have a visible scope restricted to that function and its children.

「localは関数内でのみ使用可能で、変数名の可視スコープをその関数とその子関数に限定する。」
とあることから、関数内でのみ有効な変数を宣言することができ、宣言した変数が別の関数やスクリプト全体に影響を与えることを防ぐことができます。

trapとは

出力されたスクリプトの_direnv_hook()内で使われており、Bashのマニュアル4.1 Bourne Shell Builtinsに記載があります。

_direnv_hook()
_direnv_hook() {
  〜省略〜
  trap -- '' SIGINT;
  〜省略〜
  trap - SIGINT;
  〜省略〜
};

trap [-lp] [arg] [sigspec …]
The commands in arg are to be read and executed when the shell receives signal sigspec. If arg is absent (and there is a single sigspec) or equal to ‘-’, each specified signal’s disposition is reset to the value it had when the shell was started. If arg is the null string, then the signal specified by each sigspec is ignored by the shell and commands it invokes.

「arg のコマンドは、シェルがシグナル sigspec を受け取ったときに読み込まれ、実行される。 arg が存在しない (そして sigspec が 1 つである) か、'-' に等しい場合、指定された各シグナルの処分は、シェルが起動されたときの値にリセットされる。 arg が NULL 文字列である場合、各 sigspec で指定されたシグナルは、シェルとそれが呼び出すコマンドによって無視される。」
とあることから、trap -- '' SIGINTSIGINTを無視しtrap - SIGINTその無視をリセットしています。

--について
引数'--'を与えることがでオプション処理を終了させ、次の単語がオプション以外の引数であることを示し、それ以外は無視されます。これは、コマンドの引数が'-'で始まる場合に便利です。

SIGINTについて
シグナル(プロセスに送信される信号)の一つで、「キーボードからの割り込み、control + c」をプロセスに要求します。

PROMPT_COMMANDとは

出力されたスクリプトのif文で使われており、Bashのマニュアル5.2 Bash Variablesに記載があります。

if [[ ";${PROMPT_COMMAND[*]:-};" != *";_direnv_hook;"* ]]; then
  if [[ "$(declare -p PROMPT_COMMAND 2>&1)" == "declare -a"* ]]; then
    PROMPT_COMMAND=(_direnv_hook "${PROMPT_COMMAND[@]}")
  else
    PROMPT_COMMAND="_direnv_hook${PROMPT_COMMAND:+;$PROMPT_COMMAND}"
  fi
fi

PROMPT_COMMAND
If this variable is set, and is an array, the value of each set element is interpreted as a command to execute before printing the primary prompt ($PS1). If this is set but not an array variable, its value is used as a command to execute instead.

「この変数が設定されていて配列である場合、設定されている各要素の値は、 主プロンプト($PS1)を表示する前に実行するコマンドとして解釈される。 この変数が設定されているが配列変数でない場合は、 その値が代わりに実行するコマンドとして使われる。」
とあることから、変数PROMPT_COMMANDに関数を設定することで、主プロンプト($PS1)を表示する前に関数を実行することができます。

また、if [[ ";${PROMPT_COMMAND[*]:-};" != *";_direnv_hook;"* ]]; thenPROMPT_COMMANDに_direnv_hookが存在するか確認し、存在しない場合はPROMPT_COMMANDに_direnv_hookを設定しています。

IFS変数が空の場合、;区切りで展開されない
macOSの場合、配列ではなくテキストなので問題ない
Linux環境での挙動を試してみる
direnvでバグを見つけた場合の修正依頼はどうすればいい?

direnv hook bash で出力されるスクリプトがしていること

  • BashのPROMPT_COMMANDに_direnv_hookが存在するか確認し、存在しない場合に設定
  • _direnv_hookではtrap -- '' SIGINTでSIGINTを無視しdirenv export bashを実行、trap - SIGINTでその無視をリセット

まとめ

いかがでしたでしょうか?

direnv hook bashでどんなスクリプトが出力されているかだいぶイメージできるようになりました。

_direnv_hookで実行しているdirenv export bashも少し気になりますが、exportとあることから環境変数を有効にしているのだろうと理解しました。

direnvについて詳しく調べることで、ますますdirenvを使いたくなりました。
今後もどんどん使っていきたいと思います。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?