The Will Will Web

記載著 Will 在網路世界的學習心得與技術分享

關於 PATH 環境變數過長而導致命令提示字元下無法執行特定程式的問題

最近由於經常在研究前端常用的各式工具,像是 npm, bower, gulp, webpack, yo, git 等等的工具用的很多,因此親手打指令執行程式的機會變多了,然而有好長一段時間間斷地出現了一些靈異現象,我卻無法解釋,直到最近終於發現了一些潛在的地雷,身處 Windows 平台下的前端工程師們不得不注意。由於我最近迷上了 Cmder 工具,在使用這套工具時可以讓我在輸入指令時,就像身在 Linux 環境下一樣自在,功能很多、支援鍵盤快速鍵、還支援命令與參數自動完成,非常好用,不過有時候卻發現他有點不太靈光,明明已經裝好了 npm 或 bower 工具,就是無法正常執行,我開啟 Windows 內建的「命令提示字元」視窗卻又可以執行。但問題是並不是永遠都不能執行,因為有時候又可以正常執行。這完全是一個農曆七月鬼打牆的概念,我將用這篇文章來說明問題背後的原理與解決方法。

問題描述

我們先來看看打開 Cmder 之後無法執行 npm, bower 的畫面:

image

如果此時你打開 Cmd.exe 命令提示字元,卻會發現程式明明是可以執行的:

image

我遇到這個問題時覺得很納悶,第一時間會認為是不是 PATH 環境變數沒設定好?或是被清除掉了?但進一步檢查後卻發現明明都有設定好啊,為什麼就是 Cmder 不能用呢?

後來我認真的把 PATH 從環境變數的設定介面中全部複製到文字編輯器裡,仔細了檢查一下,除了 PATH 路徑變得很長外,其他基本上沒什麼異狀。

image

然而,我在第一時間並沒有意識到 PATH 環境變數原來是有長度限制的,所以花了我好多時間研究為什麼會這樣。

最後我試著將依些很少用的執行檔路徑從 PATH 環境變數中移除,很神奇的 Cmder 就可以正常運作了,這時才終於知道原來 Windows 作業系統下不能設定太長的 PATH 環境變數!

 

發生原因

你可能會想說,是不是最近安裝了很多前端工具,所以把 Windows 環境變數搞得特別長呢?答案:不是!

發生這個問題的原因根本不在這些前端工具的安裝過程中,而是我的電腦在經年累月安裝各式軟體的過程中,PATH 環境變數不斷累加上去各個軟體的執行檔路徑,而最近剛好達到這個 2,048 字元的臨界點而已。所以當我在執行 Cmd.exe 命令提示字元的時候,我的工作階段下的 PATH 環境變數還沒超出他的長度限制,所以程式都可以正常執行。

我後來研究了一下,原來 Cmder 會在啟動的時候,自動先執行一個 init.bat 批次檔,這個批次檔也會在工作階段下的 PATH 環境變數加入一些其他的路徑(如下圖示),而意外地讓 PATH 環境變數超出 Windows 的限制,也就是 2,048 字元。

image

你可能更會納悶的是,什麼應用程式路徑這麼長,長到會有機會超過 2,048 這麼長呢?不說你不知道,你可以現在就打開你的命令提示字元,輸入一下 echo %PATH% 就可以知道你的 PATH 環境變數有多長了! ^^

※ 你也可以執行 ECHO.%PATH:;= & ECHO.% 輸出每行一個路徑。

解決方案

解決方法別無他法:你只能想辦法將 PATH 環境變數縮短

首先,你必須先搞懂環境變數分兩種:

image

  • 使用者變數
    • 每位使用者都會有一組環境變數設定,所以屬於該使用者需要的環境變數請設定在這裡。
  • 系統變數
    • 所有使用者都會共用的環境變數設定,全部都設定在這裡,所以使用者登入都會一律載入這裡定義的環境變數。
    • 不僅僅一般使用者,包含所有系統使用者或一些服務帳號執行的程式,預設都會載入系統環境變數。

另外,PATH 環境變數套用的順序是優先套用 系統變數 再套用 使用者變數,意思也就是說 系統變數 的優先權較高。

 

方法 1: 減少重複定義的路徑

  • 你仔細去比對兩邊的設定,會發現重複率還蠻高的,你可以試圖刪除一些重複的路徑。

 

方法 2: 刪除無用的路徑

  • 有些應用程式在移除之後並不會自動從 PATH 環境變數設定中移除路徑。
  • 有些應用程式同時安裝了多個版本,但你其實只需要用到最新版,這些舊版路徑也可以移除。
    • 例如我的 SQL Server Management Studio 同時安裝過 2012, 2014 與 2016 版,所以在我的 PATH 環境變數中同時存在了 C:\Program Files (x86)\Microsoft SQL Server\110\DTS\Binn\ 與 C:\Program Files (x86)\Microsoft SQL Server\120\DTS\Binn\ 與 C:\Program Files (x86)\Microsoft SQL Server\130\DTS\Binn\,舊版的路徑根本是不需要的,因為當我有需要在命令提示字元輸入 ssmssqlcmd 時,一定是想執行最新版(因為相容性較高,新舊都能用),這時你就可以將舊的路徑刪除。
    • 你可以用 where ssmswhere sqlcmd 查詢執行檔所在路徑,如果出現多筆,就代表你的 PATH 環境變數額外定義了一些不必要的路徑,第一順位的路徑就是你會執行的程式。如下圖示:
      image
    • 你很難想像,光是這樣一個檢查動作,可以刪除超過 2,000 個字元!!!

除了上述方法外,由於系統層級的 PATH 環境變數通常會長很多,我個人還會把一些註冊在系統 PATH 環境變數的路徑搬到使用者層級的 PATH 環境變數中,這樣也有助於降低系統層級 PATH 環境變數的字元數。

 

技術細節

我是個熱愛追求細節的人,對於為什麼是 2,048 字元呢?我很想了解!

事實上,Windows 環境下的所有環境變數的總長度限制是 32,760 字元,這可是包含「所有的環境變數」(包含變數名稱與值),所以如果你完全沒有其他環境變數,你大可設定一個 PATH 環境變數,且長度可以長到 32,754 個字元。

另外,你在批次檔中執行程式時,PATH 環境變數的總長度會占用到「最大單一命令長度」的字元限制,這個限制有 32,767 字元,所以執行時也不能超過這個數字。

然後上述這兩點都不是 2,048 字元的限制,照理來說 PATH 可接受的字元數應該會更長不是嗎?

事實上是這樣的,當你在控制台設定環境變數時,這些環境變數都是存到 系統機碼 (Registry) 中的,雖然系統機碼的字串值可以儲存超過 2,048 字元,但「命令提示字元」在從機碼讀取 PATH 環境變數時最多只能讀取 2,048 字元,超出這個數字的字元全部都會被截斷,所以之前有讀到的程式可以執行,但之後被截斷的路徑由於沒有被完整讀入,所以打指令時會找不到路徑,這就是靈異事件的關鍵啊!

  • 「命令提示字元」在從機碼讀取 PATH 環境變數時最多只能讀取 2,048 字元
  • 「命令提示字元」在從機碼讀取 PATH 環境變數時最多只能讀取 2,048 字元
  • 「命令提示字元」在從機碼讀取 PATH 環境變數時最多只能讀取 2,048 字元

另外,你在環境變數對話框上(如下圖),是可以顯示的 PATH 環境變數超過 2,048 字元的,但從這個輸入框卻無法輸入超過 2,048 字元:

image

你若透過 SET 命令設定 PATH 環境變數,長度並沒有 2,048 字元限制,因為這個 PATH 環境僅套用在當下工作階段中。但你若使用其他工具程式或 SETX 工具,是可以寫入 PATH 環境變數超過 2,048 字元的 (寫入到系統機碼中)。

你看看、你看看,這種可以寫入機碼,但卻無法讀取超過 2,048 字元這種是,是不是有種被 Windows 惹惱的感覺,這真的好雷啊~ >"<

 

知道了這些細節後,我想這個問題以後終於不會再鬼打牆了,這真是個好特別的冷知識啊! ^_^

 

相關連結