﻿# 如何透過 PowerShell 自動寫入執行檔路徑到 PATH 使用者環境變數

今天看到微軟推出 [Azure Developer CLI](https://devblogs.microsoft.com/azure-sdk/introducing-the-azure-developer-cli-a-faster-way-to-build-apps-for-the-cloud/?WT.mc_id=DT-MVP-4015686) 工具 (預覽版) 的消息，便立即安裝起來玩看看，而在安裝的過程中有發現他有用 PowerShell 寫入 `PATH` 環境變數，我就把安裝程式解析了一下，發現有一段 Code Snippet 相當實用，以後寫安裝程式的時候就可以直接沿用了。

![PATH](https://stwillblogassets.blob.core.windows.net/files/images/external/user-images.githubusercontent.com/2153c0282563453170f8-179254058-dac1f8b7-23ef-4a51-8a4f-75d01c7dcc44.png)

以下是 [Azure Developer CLI](https://github.com/Azure/azure-dev) 的安裝命令：

```powershell
powershell -c "Set-ExecutionPolicy Bypass Process -Force; irm 'https://aka.ms/install-azd.ps1' | iex"
```

其中 `irm` 是 [Invoke-RestMethod](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/invoke-restmethod?view=powershell-7.2&WT.mc_id=DT-MVP-4015686) 的 Alias，用來抓取 [https://aka.ms/install-azd.ps1](https://aka.ms/install-azd.ps1) 這個 PowerShell 安裝指令檔，而將內容 pipe 給 `iex` ([Invoke-Expression](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/invoke-expression?view=powershell-7.2&WT.mc_id=DT-MVP-4015686)) 則是用來直接執行 PowerShell 命令。

你也可以先下載 `install-azd.ps1` 回來，然後提供參數執行安裝命令：

```powershell
irm 'https://aka.ms/install-azd.ps1' -OutFile install-azd.ps1
.\install-azd.ps1 -NoPath
```

這支 PowerShell 安裝程式最讓我感興趣的地方，其實是設定 `PATH` 環境變數的地方，因為無論是 Command Prompt 環境或是 PowerShell 環境，在設定環境變數這件事情上，一直都沒有完美的解決方案，網路上可以找到的解決方案，我可以說 `99%` 都有瑕疵，無法應付各種設定 `PATH` 環境變數的情境，這個比例一點也不誇張！

在這之前，我所找到的解決方案是透過一個相當不知名的 [SetEnv](https://github.com/doggy8088/SetEnv/) 命令列工具，這是我測試過可以符合所有情境的 `PATH` 設定工具，我在過往的好幾篇文章中都有用到。

這套 `SetEnv.exe` 的使用方式如下：

```powershell
# 先用 PowerShell 下載設定環境變數的利器 SetEnv（你也可以手動下載）
Invoke-WebRequest -Uri "https://github.com/doggy8088/SetEnv/releases/download/1.0/SetEnv.exe" -OutFile "SetEnv.exe"

# 增加 PATH 使用者環境變數（這段命令支援 PowerShell 也支援 Command Prompt 環境）
SetEnv.exe -ua PATH %"C:\Program Files\Git\usr\bin"
```

由於這個解決方案需要下載額外的 `SetEnv.exe` 執行檔，我們有些客戶的部署環境是完全無法下載 `*.exe` 執行檔的，所以這個解決方案一直不是太理想。

今天我從 [install-azd.ps1](https://aka.ms/install-azd.ps1) 擷取出以下程式碼片段：

```powershell
# 這個 $InstallFolder 變數用來指定你要加入 PATH 環境變數的路徑
$InstallFolder = 'C:\Program Files\Git\usr\bin'

# $env:Path, [Environment]::GetEnvironmentVariable('PATH'), and setx all expand
# variables (e.g. %JAVA_HOME%) in the value. Writing the expanded paths back
# into the environment would be destructive so instead, read the path directly
# from the registry with the DoNotExpandEnvironmentNames option and write that
# value back using the non-destructive [Environment]::SetEnvironmentVariable
# which also broadcasts environment variable changes to Windows.
try {
    $registryKey = [Microsoft.Win32.Registry]::CurrentUser.OpenSubKey('Environment', $false)
    $originalPath = $registryKey.GetValue(`
        'PATH', `
        '', `
        [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames `
    )
    $pathParts = $originalPath -split ';'

    if (!($pathParts -contains $InstallFolder)) {
        Write-Host "Adding $InstallFolder to PATH"

        # SetEnvironmentVariable broadcasts the "Environment" change to
        # Windows and is NOT destructive (e.g. expanding variables)
        [Environment]::SetEnvironmentVariable(
            'PATH', `
            "$originalPath;$InstallFolder", `
            [EnvironmentVariableTarget]::User`
        )

        # Also add the path to the current session
        $env:PATH += ";$InstallFolder"
    } else {
        Write-Host "An entry for $InstallFolder is already in PATH"
    }
} finally {
    if ($registryKey) {
        $registryKey.Close()
    }
}
```

這段程式可以非常完美的解決設定 `PATH` 環境變數的問題，實在是太棒了！ 😍

### 相關連結

-   Azure Developer CLI
    -   [Introducing the Azure Developer CLI: A faster way to build apps for the cloud](https://devblogs.microsoft.com/azure-sdk/introducing-the-azure-developer-cli-a-faster-way-to-build-apps-for-the-cloud/?WT.mc_id=DT-MVP-4015686)
    -   [What is the Azure Developer CLI (azd) Preview?](https://learn.microsoft.com/en-us/azure/developer/azure-developer-cli/overview?WT.mc_id=DT-MVP-4015686&tabs=csharp)
    -   [Azure Developer CLI - Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=ms-azuretools.azure-dev)
    -   [Azure/azure-dev](https://github.com/Azure/azure-dev): A developer CLI that accelerates the time it takes for you to get started on Azure. The Azure Developer CLI provides a set of developer-friendly commands that map to key stages in your workflow - code, build, deploy, monitor, repeat.
-   Microsoft.PowerShell.Utility
    -   [Invoke-RestMethod](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/invoke-restmethod)
    -   [Invoke-Expression](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/invoke-expression)
