Bisakah saya mengubah PATH saya secara dinamis berdasarkan cwd saya?

4

Saya ingin mengubah $PATHvariabel lingkungan saya tergantung pada direktori kerja saya saat ini.

Katakan saya masuk /foo/bar/bazdan saya punya direktori /foo/node_modules/.bindan /foo/bar/baz/node_modules/.bin. Saya ingin menambahkan setiap kemungkinan ./node_modules/.binsecara rekursif $PATH.

Tetapi ketika saya cdmasuk ke direktori yang berbeda (seperti /foo/bar), saya ingin dokumen asli saya, bersih $PATHuntuk dipulihkan, dan kemudian mulai mencari ./node_modules/.binsecara rekursif lagi.

(Saya ingin menyelesaikan pertanyaan saya sendiri dari pelacak masalah npm: Bisakah kita menambahkan paket yang diinstal secara lokal ke PATH juga? )

Catatan: Saya menggunakan Mac, tetapi tertarik pada solusi umum.

Pipo
sumber

Jawaban:

3

pengantar

Jika saya mengerti Anda benar, Anda ingin menambahkan direktori "$X/node_modules/.bin"mana $Xadalah $PWDatau dari nenek moyangnya.

Script di akhir posting ini harus memberikan perilaku yang Anda inginkan. Anda perlu sumber di setiap sesi di mana Anda inginkan. Jika Anda memberi nama file augment_path.sh, maka menambahkan baris ini ke Anda .bashrcharus cukup:

source augment_path.sh

Diskusi

Saya pikir garyjohn memiliki pendekatan dasar yang benar, tetapi dia mencari semua keturunan daripada semua leluhur.

The $PROMPT_COMMANDvariabel memungkinkan Anda untuk menentukan perintah yang akan dieksekusi setiap kali prompt ditampilkan. Saya telah menambahkan $PROMPT_COMMAND_OLDvariabel untuk memungkinkan yang asli $PROMPT_COMMANDdikembalikan

Ini mungkin tidak perlu, tetapi untuk bentuk yang baik saya menambahkan $LAST_WDvariabel dan menguji untuk menghindari mengkompilasi ulang jalur ketika direktori belum berubah. Anda dapat menghapus ketiga baris yang berisi LAST_WDjika Anda mau.

The augment_pathscan fungsi dari $PWDatas, mencari direktori target dalam setiap nenek moyang dan menambahkan setiap itu menemukan jalan.

  • Mereka ditempatkan di jalan secara berurutan, sehingga direktori tersebut yang paling dalam akan diutamakan jika ada konflik. Saya menganggap ini adalah perilaku yang diinginkan. Jika tidak, ubah

    PATH_ADDITION="$PATH_ADDITION:$resolved_target"

    untuk

    PATH_ADDITION="$resolved_target:$PATH_ADDITION"
  • Namun, direktori ini semua akan diutamakan dari sisa jalan. Jika Anda ingin sisa jalur diutamakan, ubah:

    PATH="$PATH_ADDITION:$RAW_PATH"

    untuk

    PATH="$RAW_PATH:$PATH_ADDITION"

Naskah

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"
pyrocrasty
sumber
Terima kasih semua orang yang terlibat dalam memecahkan masalah ini dan terima kasih sangat besar atas jawaban mendalam ini. Sebagai non-shell-expert, penjelasan tambahan Anda sangat membantu. Solusi Anda bekerja persis seperti yang saya harapkan (direktori terdalam dengan prioritas tertinggi dan prioritas lebih tinggi dari sisa jalur. Sempurna.
Pipo
1

Saya pikir memasukkan hal berikut ke dalam ~/.bashrckehendak Anda melakukan apa yang Anda inginkan.

Jika variabel lingkungan bash PROMPT_COMMANDdiatur ke beberapa perintah, perintah itu dijalankan sebelum bash menampilkan prompt utama.

Dalam kode di bawah ini, PROMPT_COMMANDdiatur ke nama fungsi doit,. Fungsi itu memeriksa apakah direktori kerja saat ini telah berubah dan jika demikian pertama-tama set PATHke nilai aslinya, kemudian memeriksa keberadaan subdirektori bernama node_modules/.bin. Jika subdirektori itu ada, fungsi menambahkan nama-nama itu dan semua subdirektori di bawahnya 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

garyjohn
sumber
Saya sorrty, saya lupa menyebutkan bahwa saya menggunakan Mac OS X. Sepertinya saya tidak bisa menggunakannya di -printfsana. Selain itu saya pikir itu akan berhasil ...
Pipo
Mengganti -printf ':%p'dengan -exec echo -n :{} \;tampaknya berfungsi juga, tetapi tidak semua echomemiliki -ndan saya tidak memiliki akses ke Mac saat ini. Jika Anda echotidak memiliki -natau yang setara, Anda dapat menghilangkan -ndan ikuti finddengan | tr -d '\n'untuk menghapus baris baru.
garyjohn
-n sepertinya berhasil. Ini berfungsi hampir seperti yang saya inginkan, tetapi tidak bekerja secara rekursif dari direktori paling dalam ke atas atau apakah saya salah melakukannya? Katakan saya masuk foo/bar/bazdaripada tidak /foo/bar/baz/node_modules/.bindan /foo/node_modules/.binditambahkan.
Pipo
0

Anda dapat bekerja dengan bash PROMPT_COMMAND:

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

Perintah itu dijalankan setiap kali ketika prompt muncul. Oleh karena itu, setiap kali ketika perintah selesai, PATHvariabel diubah. Direktori tambahan ditambahkan ke bagian akhir.

Jika tidak ada direktori ./node_modules/.bindi direktori kerja saat ini, jalur tetap ditambahkan. Tapi, itu bukan masalah. Itu tidak dicari melalui (karena tidak ada).

Lihat demonstrasi:

$ 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
kekacauan
sumber
Ini berfungsi hampir seperti yang saya inginkan, tetapi tidak bekerja secara rekursif dari direktori paling dalam ke atas atau apakah saya salah melakukannya? Katakan saya masuk foo/bar/bazdaripada tidak /foo/bar/baz/node_modules/.bindan /foo/node_modules/.binditambahkan.
Pipo
@Pipo Saya tidak mengerti dengan benar. Anda ingin menambahkan 2 folder? Jadi, ./node_modules/.bindan ../../node_modules/.bin? Jika ya, gunakan itu PROMPT_COMMAND='[ -z "$X" ] && X=$PATH; PATH=$X:$(pwd)/node_modules/.bin:$(pwd)/../../node_modules/.bin'. Jika akan terlihat agak aneh saya $PATHvariabel tetapi harus bekerja, seperti yang diinginkan.
kekacauan
Ya kamu benar. Saya ingin menambahkan kedua folder, karena modul Node saling bersarang. Terima kasih, saya akan coba itu.
Pipo
Saya hanya memperhatikan bahwa ../../bagian itu hardcoded. Maaf, saya seharusnya mengatakan bahwa /foo/bar/bazcontoh saya hanyalah satu contoh. Itu benar-benar bisa menjadi salah struktur direktori. Saya akan mencoba jawaban @pyrocrasty sekarang.
Pipo