Как установить переменную среды PATH в пакетном файле только один раз в Windows?

У меня есть командный файл, который задает путь пользователя и запускается как часть этапа сборки 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:

Почему этот путь также отображается как добавленный путь как часть переменной системной среды?

При выполнении echo %PATH% через консоль Visual Studio (когда я запускаю проект второй раз) печатает путь к системной переменной и добавленный к нему новый путь.

Вопрос 2:

Я хочу изменить свой командный файл так, чтобы он устанавливал только переменную среды PATH в пользовательских настройках во время первого запуска сборки Visual Studio. Если пользовательская переменная PATH уже существует при последующих запусках, она не должна снова выполнять команду установить, чтобы избежать добавления нового пути снова и снова в системную переменную.

Любые идеи, как достичь этого?

Ответ 1

edit: После некоторого тестирования выясняется, что мой первоначальный ответ не совсем применим к вопросам OP. Чтобы напрямую ответить OP:

  • %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. Но не делайте этого, если не хотите создавать серьезные проблемы при установке вашей операционной системы.

  • Если вы хотите проверить, существует ли каталог в %PATH%, вы могли бы cd или pushd оба в каталог, который вы хотите проверить, и каждый каталог в %PATH%, чтобы объединить их, что делает убедитесь, что все относительные пути, переменные среды и т.д. сплющены. 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"

Но это не совсем то, что спросил ОП, и я извиняюсь за ответ на коленный рефлекс.


оригинальный ответ: setx является разрушительным и не должен использоваться таким образом. Когда вы setx PATH, вы преобразовываете тип данных значения реестра из REG_EXPAND_SZ в REG_SZ. Как только вы это сделаете, все динамические переменные среды, хранящиеся в вашем% PATH%, будут преобразованы в плоские, абсолютные пути. Используйте команду Path для временного добавления каталогов на ваш %PATH% и reg add, чтобы сделать это постоянно. (В качестве дополнительной заметки также есть dpath, который временно добавляет каталог к ​​вашему пути, но может использоваться только командой type. Прокрутка 2/3 вниз эту страницу для получения дополнительной информации о dpath.)

Здесь утилита script я написал, чтобы добавить каталоги в мой %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

Ответ 2

  • Что делает Setx. См. Setx /?, где он сообщает об этом. Он постоянно добавляет его в пользовательскую среду (если не используется /m ). Однако% PATH% построено из системной и пользовательской среды PATH (а также autoexec.bat на 32-битных окнах, а если начато через ShellExecute также ключ реестра приложений),

  • Не беспокойтесь. Setx не добавляет пути к% PATH%, если они уже существуют в% PATH%.

Почему вы переопределяете системную переменную% COMSPEC%.