How to add a directory to your PATH

Julia Evans

如何將目錄加入 PATH

今天我和朋友聊到如何將目錄加入 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 設定檔

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

  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,更新 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 的開頭,方法是在你的 shell 設定檔中加入這一行(是 /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(或 zsh、或 fish)來啟動新的 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 持續儲存歷史紀錄,而不是只在離開 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 議題留言中的說明中有相關說明)。

結語

希望這對一些人有幫助。如果你知道在將目錄加入 PATH 時還有哪些主要的陷阱曾讓你遇到問題,或對這篇文章有任何疑問,歡迎告訴我(在 Mastodon 或 Bluesky 上)!

原文由 Julia Evans 發布

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