【问题标题】:How to set PATH environment variable in batch file only once on Windows?如何在 Windows 上仅在批处理文件中设置 PATH 环境变量一次?
【发布时间】:2015-10-31 09:08:48
【问题描述】:

我有设置用户路径并作为 Visual Studio IDE 构建步骤的一部分运行的批处理文件。

@ECHO OFF
@ECHO %PATH%
set COMSPEC = "%VCINSTALLDIR%\vcvarsall.bat" amd64
setx PATH "..\..\lib\libsndfile;..\..\lib\simulink" 
@ECHO %PATH%

当我构建项目时,关闭 VS,然后重新打开它,然后重新构建,我看到 附加路径作为PATH 变量的一部分。但是,我看到在 Windows 环境变量的设置中 PATH 变量是在用户环境变量下创建的

..\..\lib\libsndfile;..\..\lib\simulink

问题 1:

为什么此路径也显示为附加路径,作为系统环境变量的一部分?

在通过 Visual Studio 控制台执行echo %PATH% 时(当我第二次运行项目时)打印系统变量路径和我创建的新路径附加到它。

问题 2:

我想修改我的批处理文件,使其在首次运行 Visual Studio 构建期间仅在用户设置中设置一次 PATH 环境变量。如果后续运行时已经存在用户变量PATH,则不应再次执行set命令,以免在系统变量中一次又一次地追加新路径。

任何想法如何实现这一目标?

【问题讨论】:

    标签: batch-file


    【解决方案1】:

    编辑:经过一些测试,看来我的原始答案并不完全适用于 OP 的问题。更直接地回答OP:

    1. %PATH%HKLM\System\CurrentControlSet\Control\Session Manager\Environment\Path 中的值与HKCU\Environment\Path 组合在一起。当您setx "dir;dir" 时,您设置的是HKEY_CURRENT_USER Path 值。机器范围内的 HKEY_LOCAL_MACHINE Path 值保持不变。这就是为什么您将您的值视为附加而不是替换的原因。您必须使用 setx /m 替换 HKLM Path 值。 但请不要这样做,除非您想对操作系统安装造成严重问题。

    2. 1234563确保所有相对路径、环境变量等都被展平。 set "var=%CD%" 每个。然后if /I "!dir1!"=="!dir2!" 目录已经存在于%PATH% 的某个地方。我在下面的原始答案中有一个例子。

    我最初的答案并不完全适用的原因是setx 本身并不像我曾经想象的那样具有破坏性。危险在于,通常当用户想要将目录附加到他们的路径时,他们会setx /m PATH "%PATH%;new dir";这破坏性的。因为%PATH%setx写入值之前展开,所以PATH中的所有目录都过早展开。

    以下方法会更安全:

    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"
    

    但这并不是 OP 真正要求的,我为下意识的回答道歉。


    原始答案: setx 具有破坏性,不应以这种方式使用。当您setx PATH 时,您将注册表值数据类型从 REG_EXPAND_SZ 转换为 REG_SZ。执行此操作后,存储在 %PATH% 中的所有动态环境变量都会转换为平坦的绝对路径。使用path 命令临时将目录附加到您的%PATH%,并使用reg add 永久附加目录。 (附带说明一下,还有dpath,它会临时将目录添加到您的路径中,但只能由type 命令使用。向下滚动2/3 this page 以获取有关dpath 的更多信息.)

    这是我编写的实用程序脚本,用于以破坏性较小的方式将目录添加到我的%PATH%。它还将避免多次将同一目录添加到 %PATH%,无论其格式如何(例如尾随反斜杠、相对路径、环境变量或任何其他排列)。

    @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
    

    【讨论】:

    • 我用其中一条路径调用了您提供的上述脚本。第一次,它给出了“警告:%%PATH%% 已更改。重新打开控制台以继承更改。下一次,使用相同的参数调用它并没有给出消息“路径中已经存在”,而是添加了两次相同的路径。这发生在命令提示符和 VS 预构建事件调用中。
    • @user915783 我只是碰巧想到,脚本可能需要以海拔运行。可以试试以管理员身份运行,看看运气好不好?
    • 嗨罗乔。我将您的脚本添加到link 并确保它确实打印了添加的路径。但是,如果我进入注册表并查看添加的路径,我在任何地方都找不到“PATH”变量下的那些。事实上。我在任何地方都找不到这些路径。那么,路径在哪里?其次,当我从 VS IDE 预构建事件中运行脚本时 call setdllpathSafe.bat "..\..\lib\libsndfile" & call setdllpathSafe.bat "..\..\lib\simulink",它在给出 mssg 之前将当前路径打印三次?
    • @rojo setx 的哪个版本将注册表值的类型从REG_EXPAND_SZ 更改为REG_SZ?阅读您的答案后,我想为这项任务改编my batch solution。但是我再次尝试了我的批处理代码,在 Windows 7 SP1 x64 上使用 setx 和文件版本 6.1.7600.16385 在批处理执行之前和之后查看注册表值的类型Path。但是每次修改后注册表值类型都没有改变REG_EXPAND_SZ。在 Process Monitor 中可以看到 setx 首先查询 Path 然后将其设置为正确的类型。
    • @Mofi 相同版本。转到HKLM\System\CurrentControlSet\Control\Session Manager\Environment 并创建一个新的REG_EXPAND_SZ 值,将其命名为“foo”并将其数据设置为“bar”。保持该窗口打开,但打开一个控制台窗口并执行setx /m foo bar。现在返回注册表编辑器窗口并刷新。 foo 现在是类型 REG_SZ。但我确实看到,如果我执行setx /m foo %bar%(其中未定义%bar%),setx 会将数据类型设置回REG_EXPAND_SZ。我忘记了在哪里读到 setx 的破坏性,但直到现在我还没有真正尝试过它。
    【解决方案2】:
    1. 这就是 Setx 所做的。请参阅Setx /? 它告诉您这一点。它将它永久添加到用户的环境中(除非使用 /m)。但是 %PATH% 是从系统和用户环境的 PATH 构建的(以及 32 位窗口上的 autoexec.bat,如果通过 ShellExecute 启动,还有 App Paths reg 键))

    2. 别担心。如果 %PATH% 中已存在路径,Setx 不会添加到 %PATH% 的路径。

    为什么要重新定义系统变量 %COMSPEC%。

    【讨论】:

    • 感谢您的两个回答。我正在重新定义,以便后续的路径添加可以在 Visual Studio 中成功进行。虽然您的回答 2 解决了我的问题,但出于好奇,有没有办法在下次运行批处理文件时从 %PATH% 中删除附加路径的一部分?
    • 您当然可以使用reg 命令和wmic 通过阅读编辑并写回您想要的内容来做到这一点。问题是 Windows(这意味着资源管理器)在注销/打开之前不会知道它已更改。但是您可以使用reg 删除并使用setx 恢复。 Setx 向监控它的程序发送系统更改事件,并且资源管理器重建它的路径,因此它启动的任何程序(如 CMD)都会有新路径。
    猜你喜欢
    • 2014-03-03
    • 2011-04-17
    • 2017-03-30
    • 1970-01-01
    • 2017-02-21
    • 1970-01-01
    • 1970-01-01
    • 2014-09-14
    • 1970-01-01
    相关资源
    最近更新 更多