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

后端 未结 2 1893
臣服心动
臣服心动 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:33
    1. That what Setx does. See Setx /? where it tells you this. It adds it to the user's environment permanently (unless /m is used). However %PATH% is built from system and user environment's PATH (and also autoexec.bat on 32 bit windows and if started through ShellExecute also App Paths reg key))

    2. Don't worry about. Setx doesn't add paths to %PATH% if they already exist in %PATH%.

    Why are you redefining the system variable %COMSPEC%.

    0 讨论(0)
  • 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 "\<Path\>"'
    ) 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 <val>
    set "env=HKLM\System\CurrentControlSet\Control\Session Manager\Environment"
    for /f "tokens=2*" %%I in ('reg query "%env%" /v Path ^| findstr /i "\<Path\>"') 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
    
    0 讨论(0)
提交回复
热议问题