How to set PATH environment variable in batch file only once on Windows?

后端 未结 2 1917
臣服心动
臣服心动 2020-11-28 16:10

I have batch file that sets user path and is run as part of Visual Studio IDE build step.

@ECHO OFF
@ECHO %PATH%
set COMSPEC = \"%VCINSTALLDIR%\\vcvarsall.ba         


        
2条回答
  •  难免孤独
    2020-11-28 16:47

    edit: After some testing, it appears that my original answer isn't entirely applicable to OP's questions. To answer OP more directly:

    1. %PATH% combines the values in HKLM\System\CurrentControlSet\Control\Session Manager\Environment\Path with HKCU\Environment\Path. When you setx "dir;dir", what you're setting is the HKEY_CURRENT_USER Path value. The machine-wide HKEY_LOCAL_MACHINE Path value remains untouched. That's why you see your values as appended, rather than as replacements. You'd have to use setx /m to replace the HKLM Path value. But please don't unless you want to create severe problems with your operating system installation.

    2. If you want to test whether a directory exists in %PATH%, you could cd or pushd both to the directory you want to check and to each directory within %PATH% to unify each, making sure all relative paths, environment variables, etc. are flattened. set "var=%CD%" for each. Then if /I "!dir1!"=="!dir2!" the directory already exists somewhere in %PATH%. There's an example of this in my original answer below.

    The reason my original answer isn't entirely applicable is because setx itself isn't as destructive as I once thought. The danger is that often times when users want to append a directory to their path, they'll setx /m PATH "%PATH%;new dir"; and that is destructive. Because %PATH% is expanded before setx writes the value, all the directories in PATH are expanded prematurely.

    The following method would be safer:

    set "env=HKLM\System\CurrentControlSet\Control\Session Manager\Environment"
    
    for /f "tokens=2*" %%I in (
        'reg query "%env%" /v Path ^| findstr /i "\"'
    ) do setx /m PATH "%%J;new directory"
    

    But that wasn't really what OP asked, and I apologize for the knee-jerk answer.


    original answer: setx is destructive and shouldn't be used this way. When you setx PATH you're converting the registry value data type from REG_EXPAND_SZ to REG_SZ. As soon as you do this, all the dynamic environment variables stored in your %PATH% get converted to flat, absolute paths. Use the path command to append directories to your %PATH% temporarily, and reg add to do so permanently. (As a side note, there's also dpath, which temporarily adds a directory to your path, but can only be used by the type command. Scroll 2/3 the way down this page for more info on dpath.)

    Here's a utility script I wrote to add directories to my %PATH% in a less destructive manner. It will also avoid adding the same directory to %PATH% more than once, regardless of how it's formatted (e.g. trailing backslash, relative paths, environment variables, or any other permutation).

    @echo off
    setlocal enabledelayedexpansion
    
    if not exist "%~1" goto usage
    
    for %%I in ("%~1") do pushd "%%~I" 2>NUL && (set "new=!CD!" && popd) || goto usage
    for %%I in ("%PATH:;=";"%") do pushd "%%~I" 2>NUL && (
        rem // delaying expansion of !new! prevents parentheses from breaking things
        if /i "!new!"=="!CD!" (
            echo !new! already exists in %%PATH%%
            goto :EOF
        )
        popd
    )
    
    call :append_path "%new%"
    
    goto :EOF
    
    :usage
    echo Usage: %~nx0 "dir"
    goto :EOF
    
    :append_path 
    set "env=HKLM\System\CurrentControlSet\Control\Session Manager\Environment"
    for /f "tokens=2*" %%I in ('reg query "%env%" /v Path ^| findstr /i "\"') do (
    
        rem // make addition persistent through reboots
        reg add "%env%" /f /v Path /t REG_EXPAND_SZ /d "%%J;%~1"
    
        rem // apply change to the current process
        for %%a in ("%%J;%~1") do path %%~a
    )
    
    rem // use setx to set a temporary throwaway value to trigger a WM_SETTINGCHANGE
    rem // applies change to new console windows without requiring a reboot
    (setx /m foo bar & reg delete "%env%" /f /v foo) >NUL 2>NUL
    
    color 4E
    echo Warning: %%PATH%% has changed.  Reopen the console to inherit the changes.
    
    goto :EOF
    

提交回复
热议问题