cwd に基づいて PATH を動的に変更できますか?

cwd に基づいて PATH を動的に変更できますか?

$PATH現在の作業ディレクトリに応じて環境変数を変更したいと思います。

私が にいて/foo/bar/baz、ディレクトリ/foo/node_modules/.binと があるとします。 可能なものをすべてに再帰的に/foo/bar/baz/node_modules/.bin追加したいと思います。./node_modules/.bin$PATH

しかし、cd別のディレクトリ (など/foo/bar) に移動すると、元のクリーンな状態$PATHが復元され、再び再帰的に検索が開始されます./node_modules/.bin

(npm の問題追跡システムから自分の質問を解決したい:ローカルにインストールされたパッケージも PATH に追加できますか?

注: 私は Mac を使用していますが、一般的な解決策に興味があります。

答え1

導入

私の理解が正しければ、またはその祖先のいずれか"$X/node_modules/.bin"$Xあるディレクトリを追加したいのですね。$PWD

この投稿の最後にあるスクリプトは、必要な動作を実現するはずです。必要なすべてのセッションでこれをソースする必要があります。ファイルに という名前を付ける場合はaugment_path.sh、次の行を追加するだけで.bashrc十分です。

source augment_path.sh

議論

garyjohn の基本的なアプローチは正しいと思いますが、彼はすべての先祖ではなくすべての子孫を検索しています。

変数$PROMPT_COMMANDを使用すると、プロンプトが表示されるたびに実行されるコマンドを指定できます。$PROMPT_COMMAND_OLD元のコマンド$PROMPT_COMMANDを復元できるように変数を追加しました。

おそらく必要ではありませんが、$LAST_WDディレクトリが変更されていない場合にパスの再計算を回避するために、変数とテストを追加します。必要に応じて、次の 3 行をすべて削除できますLAST_WD

このaugment_path関数は$PWD上方からスキャンし、各祖先内のターゲット ディレクトリを検索し、見つかったディレクトリをパスに追加します。

  • これらはパスに順番に配置されるため、競合がある場合は最も深いディレクトリが優先されます。これが望ましい動作だと思います。そうでない場合は、変更してください。

    PATH_ADDITION="$PATH_ADDITION:$resolved_target"
    

    PATH_ADDITION="$resolved_target:$PATH_ADDITION"
    
  • ただし、これらのディレクトリはすべてパスの残りの部分よりも優先されます。パスの残りの部分を優先させたい場合は、次のように変更します。

    PATH="$PATH_ADDITION:$RAW_PATH"
    

    PATH="$RAW_PATH:$PATH_ADDITION"
    

脚本

RAW_PATH="$PATH"
LAST_WD=`pwd`

augment_path() {
    target="node_modules/.bin"
    if [ "$PWD" = "$LAST_WD" ]; then return 0; fi;
    PATH_ADDITION=""
    scandir="$PWD"
    until [ "$scandir" = "" ]; do
    resolved_target="$scandir"/"$target"
    if [ -d "$resolved_target" ]; then
        PATH_ADDITION="$PATH_ADDITION:$resolved_target"
    fi
    scandir="${scandir%/*}"
    done 
    PATH="$PATH_ADDITION:$RAW_PATH"
    LAST_WD=`pwd`
}

PROMPT_COMMAND_OLD="${PROMPT_COMMAND%; augment_path}"
PROMPT_COMMAND="$PROMPT_COMMAND_OLD; augment_path"

答え2

bashのPROMPT_COMMAND:

PROMPT_COMMAND='[ -z "$X" ] && X=$PATH; PATH=$X:$(pwd)/node_modules/.bin'

そのコマンドはプロンプトが表示されるたびに実行されます。したがって、コマンドが終了するたびに変数PATHが変更されます。最後に追加のディレクトリが追加されます。

現在の作業ディレクトリにディレクトリがない場合でも./node_modules/.bin、パスは追加されます。しかし、これは問題ではありません。パスは検索されません (存在しないため)。

デモをご覧ください:

$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
$ PROMPT_COMMAND='[ -z "$X" ] && X=$PATH; PATH=$X:$(pwd)/node_modules/.bin'
$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/root/node_modules/.bin
$ cd test
$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/root/test/node_modules/.bin
$ cd /etc/
$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/etc/node_modules/.bin

答え3

以下の内容を入力すれば、~/.bashrc希望どおりの結果が得られると私は思います。

bash 環境変数がPROMPT_COMMAND何らかのコマンドに設定されている場合、bash がプライマリプロンプトを表示する前にそのコマンドが実行されます。

以下のコードでは、PROMPT_COMMANDは関数の名前 に設定されています。doitその関数は、現在の作業ディレクトリが変更されたかどうかを確認し、変更された場合は最初に をPATH元の値に設定し、次に という名前のサブディレクトリの存在を確認しますnode_modules/.bin。そのサブディレクトリが存在する場合、関数はそのサブディレクトリの名前とその下にあるすべてのサブディレクトリの名前を に追加しますPATH

orig_path="$PATH"
prev_pwd=""
doit() {
    if [ "$PWD" != "$prev_pwd" ]; then
        PATH="$orig_path"
        if [ -d "node_modules/.bin" ]; then
            PATH="$PATH$(find $PWD/node_modules/.bin -type d -printf ':%p')"
            # For POSIX compatibility (macOS or other), use:
            # PATH="$PATH$(find $PWD/node_modules/.bin -type d -exec echo -n :{} \;)"
        fi
        prev_pwd="$PWD"
    fi
}
PROMPT_COMMAND=doit

関連情報