How to add a directory to your PATH

Julia Evans

如何將目錄加入 PATH

原文由 Julia Evans 發布,訂閱此部落格

今天我和朋友聊到如何將目錄加入 PATH。這對長年使用終端機的我來說,感覺是件「理所當然」的事,但當我實際去搜尋操作說明時,卻發現根本找不到把所有步驟都講清楚的教學——很多文章只寫「把這行加到 ~/.bashrc」,但如果你根本不是用 bash 呢?如果你的 bash 設定檔其實在別的地方呢?而且,你到底該怎麼知道要加哪一個目錄?

所以我想試著寫一份更完整的說明,也順便提一下這些年來我踩過的一些坑。

步驟 1:你正在使用哪一種 shell?

如果你不確定自己用的是哪一種 shell,可以用這個方法來確認。執行以下指令:

ps -p $$ -o pid,comm=
  • 如果你用的是 bash,它會印出 97295 bash
  • 如果你用的是 zsh,它會印出 97295 zsh
  • 如果你用的是 fish,它會顯示類似「In fish, please use $fish_pid」的錯誤($$ 在 fish 裡不是合法語法,不過光是看到這個錯誤訊息,就代表你用的是 fish,這點你大概本來就知道了)

另外,bash 是 Linux 上的預設 shell,而 zsh 是 Mac OS 上的預設 shell(以 2024 年為準)。這份說明只會涵蓋 bash、zsh 和 fish。

步驟 2:找到你的 shell 設定檔

  • 在 zsh 裡,通常是 ~/.zshrc
  • 在 bash 裡,可能是 ~/.bashrc,但情況有點複雜,請見下一節的說明
  • 在 fish 裡,通常是 ~/.config/fish/config.fish(如果你想百分之百確定,可以執行 echo $__fish_config_dir

關於 bash 設定檔的補充說明

Bash 有三個可能的設定檔:~/.bashrc~/.bash_profile~/.profile

如果你不確定你的系統實際上是用哪一個,我會建議用這個方法來測試:

  1. 在你的 ~/.bashrc 中加入 echo hi there
  2. 重新啟動終端機
  3. 如果你看到「hi there」,就代表正在使用 ~/.bashrc!太好了!
  4. 如果沒有看到,就把它刪掉,然後用同樣的方法試試 ~/.bash_profile
  5. 如果前兩個都不行,也可以試試 ~/.profile

(網路上有很多詳細的流程圖在解釋 bash 會如何決定要用哪個設定檔,但我覺得與其把它們背起來,直接動手測試才是最快、最保險的方法)

步驟 3:搞清楚要加入哪個目錄

假設你想要安裝並執行一個叫做 http-server 的程式,結果卻像這樣無法執行:

$ npm install -g http-server
$ http-server
bash: http-server: command not found

要怎麼找出 http-server 到底在哪個目錄裡?老實說,這通常沒那麼簡單——答案常常是「要看 npm 是怎麼設定的」之類的。這裡有幾個可以試的方法:

  • 通常在初次設定新的安裝工具(像是 cargonpmhomebrew 等)時,它會印出如何更新 PATH 的說明。所以如果你當時有留意,就能直接拿到指示。
  • 有時候安裝程式會自動幫你更新 shell 設定檔,直接幫你把 PATH 設定好
  • 有時候直接 Google「where does npm install things?」就能找到答案
  • 有些工具本身就有子指令可以告訴你它們被設定安裝到哪裡,例如:
    • Node/npm:npm config get prefix(然後再加上 /bin/
    • Go:go env GOPATH(然後再加上 /bin/
    • asdf:asdf info | grep ASDF_DIR(然後再加上 /bin//shims/

步驟 3.1:再次確認是正確的目錄

一旦找到你認為可能是正確的目錄,一定要確認它真的正確!舉例來說,我發現我的電腦上 http-server 位在 ~/.npm-global/bin。我可以像這樣直接執行該目錄下的 http-server 來確認:

$ ~/.npm-global/bin/http-server
Starting up http-server, serving ./public

成功了!既然你已經知道要把哪個目錄加入 PATH,就來進行下一步吧!

步驟 4:編輯你的 shell 設定檔

現在我們已經有了需要的兩個關鍵資訊:

  1. 你想加入 PATH 的是哪個目錄(例如 ~/.npm-global/bin/
  2. 你的 shell 設定檔在哪裡(例如 ~/.bashrc~/.zshrc~/.config/fish/config.fish

接下來要加入什麼,取決於你用的是哪一種 shell:

bash 設定方式:

打開你的 shell 設定檔,並加入像這樣的一行:

export PATH=$PATH:~/.npm-global/bin/

(記得把 ~/.npm-global/bin 換成你實際要加入的目錄)

zsh 設定方式:

你可以用跟 bash 一樣的方法,不過 zsh 也有稍微花俏一點的語法,如果你喜歡的話可以用:

path=(
  $path
  ~/.npm-global/bin
)

fish 設定方式:

在 fish 裡,語法不太一樣:

set PATH $PATH ~/.npm-global/bin

(在 fish 裡你也可以使用 fish_add_path,相關說明請見下方

步驟 5:重新啟動你的 shell

現在是非常重要的一步:如果你沒有重新啟動 shell,更新設定檔是不會生效的!

有兩種做法:

  1. 開啟一個新的終端機(或新的終端機分頁),最好把舊的關掉,才不會搞混
  2. 執行 bash 來啟動一個新的 shell(如果你用 zsh 就執行 zsh,用 fish 就執行 fish

我發現這兩種方法通常都沒問題。

這樣應該就完成了!試著執行你原本想跑的程式,應該就能正常運作了。

如果還是不行,以下是你可能會遇到的幾個問題:

問題 1:執行到錯誤的程式

如果執行到錯誤版本的程式,你可能需要把目錄加到 PATH 的最前面,而不是最後面。

舉例來說,我的系統上安裝了兩個版本的 python3,用 which -a 就可以看到:

$ which -a python3
/usr/bin/python3
/opt/homebrew/bin/python3

你的 shell 會使用列在最前面的那一個

如果你想用 Homebrew 的版本,就得把那個目錄(/opt/homebrew/bin)加到 PATH 的最前面,也就是在設定檔中加入這一行(是 /opt/homebrew/bin/:$PATH,而不是常見的 $PATH:/opt/homebrew/bin/

export PATH=/opt/homebrew/bin/:$PATH

或是在 fish 中:

set PATH ~/.cargo/bin $PATH

問題 2:程式不是從你的 shell 執行的

以上這些步驟只有在你從 shell 裡執行程式時才有效。如果你是從 IDE、圖形介面、cron 工作,或其他方式執行程式,就得用不同的方法把目錄加入 PATH,實際做法會依情況而定。

在 cron 工作中

有幾個選項:

  • 使用你要執行的程式的完整路徑,例如 /home/bork/bin/my-program
  • 把你想要的完整 PATH 放在 crontab 的第一行(像是 PATH=/bin:/usr/bin:/usr/local/bin:…)。你可以在 shell 中執行 echo "PATH=$PATH" 來取得目前使用的完整 PATH。

老實說,我已經很久沒遇到在 IDE/圖形介面中處理這類問題的情況,所以不太確定該怎麼處理,如果有人能指點正確方向,我會再把說明補上來。

問題 3:重複的 PATH 項目讓除錯更困難

如果你編輯了 path 然後用執行 bash(或 zshfish)的方式來啟動新的 shell,常常會產生重複的 PATH 項目,因為 shell 每次啟動時都會不斷把新的東西加到 PATH 裡。

就我個人經驗,我不覺得這種重複會真的造成什麼嚴重的問題,但如果你想搞清楚 PATH 裡到底有什麼,重複的項目會讓除錯變得比較困難。

你可以透過以下幾種方法來處理:

  1. 如果你在除錯 PATH,就在全新的終端機裡進行,這樣會得到「乾淨的」狀態,應該可以避免重複。
  2. 在 shell 設定檔的最後把 PATH 去除重複(例如在 zsh 裡據說可以用 typeset -U path 做到)
  3. 在加入目錄時,先檢查該目錄是否已經在 PATH 裡(例如在 fish 裡我相信可以用 fish_add_path --path /some/directory 做到)

如何幫 PATH 去除重複取決於你用的 shell,而且不一定有內建的方法,所以你得自己去查在你的 shell 中該怎麼做。

問題 4:更新 PATH 後遺失歷史紀錄

這是在 bash 或 zsh 中很容易遇到的情況:

  1. 執行一個指令(結果失敗)
  2. 更新你的 PATH
  3. 執行 bash 來重新載入設定
  4. 按幾下方向鍵的上鍵,想重新執行剛剛失敗的指令(或開啟一個新的終端機)
  5. 結果失敗的指令不在歷史紀錄裡!為什麼會這樣?

這是因為在 bash 中,預設要等到你離開 shell 後,歷史紀錄才會被儲存。

有幾種修正方法:

  • 與其執行 bash 來重新載入設定,不如執行 source ~/.bashrc(在 zsh 則是 source ~/.zshrc)。這樣會在目前的工作階段內重新載入設定。
  • 把 shell 設定成持續儲存歷史紀錄,而不是只有在離開時才儲存。(具體做法取決於你用 bash 還是 zsh,zsh 的歷史紀錄選項有點複雜,我也不太確定最好的方法是什麼)

關於 source 的補充說明

當你第一次安裝 cargo(Rust 的安裝工具)時,它會給你這樣的 PATH 設定說明,裡面完全沒提到具體的目錄。

This is usually done by running one of the following (note the leading DOT):

. "$HOME/.cargo/env"        	# For sh/bash/zsh/ash/dash/pdksh
source "$HOME/.cargo/env.fish"  # For fish

它的想法是,你把那一行加到 shell 設定檔裡,它們的腳本就會自動幫你設定好 PATH(以及可能的其他東西)。

這種做法很常見(例如 Homebrew 就建議你執行 brew shellenv),而你有兩種處理方式:

  1. 照著工具建議的做(例如把 . "$HOME/.cargo/env" 加到 shell 設定檔裡)
  2. 搞清楚它們叫你執行的腳本實際上會把哪些目錄加入 PATH,然後手動加入。我的做法會是這樣:
    • 在我的 shell 中執行 . "$HOME/.cargo/env"(如果用 fish 就執行 fish 的版本)
    • 執行 echo "$PATH" | tr ':' '\n' | grep cargo 來找出它加了哪些目錄
    • 看到它顯示 /Users/bork/.cargo/bin,然後縮寫成 ~/.cargo/bin
    • 用這篇文章裡的方法把 ~/.cargo/bin 這個目錄加入 PATH

我不覺得照著工具建議的做有什麼不對(那搞不好就是「最好的方法」!),但我個人通常會用第二種做法,因為我比較喜歡清楚知道自己到底改了什麼設定。

關於 fish_add_path 的補充說明

fish 有一個很好用的函式叫做 fish_add_path,你可以像這樣用它來把目錄加入 PATH

fish_add_path /some/directory

這很酷(指令這麼簡單!)但我已經不再用它了,原因有幾個:

  1. 有時候 fish_add_path 會用「universal variable」幫你更新之後所有工作階段的 PATH,有時候卻只會更新目前工作階段的 PATH,我很難分辨它到底會做哪一種。理論上文件有說明,但我看不懂。
  2. 如果你在幾週或幾個月後,因為發現弄錯了而想把某個目錄從 PATH移除,會有點麻煩(不過這個 GitHub issue 的留言裡有說明做法)。

就是這樣

希望這能幫到一些人。如果你還知道其他在把目錄加入 PATH 時容易踩到的大坑,或對這篇文章有任何問題,歡迎在 Mastodon 或 Bluesky 上告訴我!

本文章由 muse-spark-1.2-contributor 進行翻譯

留言