Есть ли команда для обновления переменных среды из командной строки в Windows? - PullRequest
420 голосов
/ 05 октября 2008

Если я изменяю или добавляю переменную среды, мне нужно перезапустить командную строку. Могу ли я выполнить команду, которая сделает это без перезапуска CMD?

Ответы [ 27 ]

127 голосов
/ 05 октября 2008

Вы можете перехватить системные переменные среды с помощью сценария vbs, но вам нужен сценарий bat для фактического изменения текущих переменных среды, поэтому это комбинированное решение.

Создайте файл с именем resetvars.vbs, содержащий этот код, и сохраните его по пути:

Set oShell = WScript.CreateObject("WScript.Shell")
filename = oShell.ExpandEnvironmentStrings("%TEMP%\resetvars.bat")
Set objFileSystem = CreateObject("Scripting.fileSystemObject")
Set oFile = objFileSystem.CreateTextFile(filename, TRUE)

set oEnv=oShell.Environment("System")
for each sitem in oEnv 
    oFile.WriteLine("SET " & sitem)
next
path = oEnv("PATH")

set oEnv=oShell.Environment("User")
for each sitem in oEnv 
    oFile.WriteLine("SET " & sitem)
next

path = path & ";" & oEnv("PATH")
oFile.WriteLine("SET PATH=" & path)
oFile.Close

создайте другое имя файла resetvars.bat, содержащее этот код, в том же месте:

@echo off
%~dp0resetvars.vbs
call "%TEMP%\resetvars.bat"

Если вы хотите обновить переменные окружения, просто запустите resetvars.bat


Апологетика

Две основные проблемы, которые у меня возникли с этим решением, были

a. Я не смог найти простой способ экспорта переменных среды из сценария vbs обратно в командную строку и

b. Переменная окружения PATH - это объединение пользовательских и системных переменных PATH.

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

Я использую странный механизм VBS + BAT + временная BAT, чтобы обойти проблему экспорта переменных из VBS.

Примечание : этот скрипт не удаляет переменные.

Возможно, это можно улучшить.

ДОБАВЛЕНО

Если вам нужно экспортировать среду из одного окна cmd в другое, используйте этот скрипт (назовем его exportvars.vbs):

Set oShell = WScript.CreateObject("WScript.Shell")
filename = oShell.ExpandEnvironmentStrings("%TEMP%\resetvars.bat")
Set objFileSystem = CreateObject("Scripting.fileSystemObject")
Set oFile = objFileSystem.CreateTextFile(filename, TRUE)

set oEnv=oShell.Environment("Process")
for each sitem in oEnv 
    oFile.WriteLine("SET " & sitem)
next
oFile.Close

Запустите exportvars.vbs в окне, которое вы хотите экспортировать из , затем переключитесь в окно, которое вы хотите экспортировать в , и введите:

"%TEMP%\resetvars.bat"
87 голосов
/ 06 сентября 2015

Вот что использует Chocolatey.

https://github.com/chocolatey/choco/blob/master/src/chocolatey.resources/redirects/RefreshEnv.cmd

@echo off
::
:: RefreshEnv.cmd
::
:: Batch file to read environment variables from registry and
:: set session variables to these values.
::
:: With this batch file, there should be no need to reload command
:: environment every time you want environment changes to propagate

echo | set /p dummy="Reading environment variables from registry. Please wait... "

goto main

:: Set one environment variable from registry key
:SetFromReg
    "%WinDir%\System32\Reg" QUERY "%~1" /v "%~2" > "%TEMP%\_envset.tmp" 2>NUL
    for /f "usebackq skip=2 tokens=2,*" %%A IN ("%TEMP%\_envset.tmp") do (
        echo/set %~3=%%B
    )
    goto :EOF

:: Get a list of environment variables from registry
:GetRegEnv
    "%WinDir%\System32\Reg" QUERY "%~1" > "%TEMP%\_envget.tmp"
    for /f "usebackq skip=2" %%A IN ("%TEMP%\_envget.tmp") do (
        if /I not "%%~A"=="Path" (
            call :SetFromReg "%~1" "%%~A" "%%~A"
        )
    )
    goto :EOF

:main
    echo/@echo off >"%TEMP%\_env.cmd"

    :: Slowly generating final file
    call :GetRegEnv "HKLM\System\CurrentControlSet\Control\Session Manager\Environment" >> "%TEMP%\_env.cmd"
    call :GetRegEnv "HKCU\Environment">>"%TEMP%\_env.cmd" >> "%TEMP%\_env.cmd"

    :: Special handling for PATH - mix both User and System
    call :SetFromReg "HKLM\System\CurrentControlSet\Control\Session Manager\Environment" Path Path_HKLM >> "%TEMP%\_env.cmd"
    call :SetFromReg "HKCU\Environment" Path Path_HKCU >> "%TEMP%\_env.cmd"

    :: Caution: do not insert space-chars before >> redirection sign
    echo/set Path=%%Path_HKLM%%;%%Path_HKCU%% >> "%TEMP%\_env.cmd"

    :: Cleanup
    del /f /q "%TEMP%\_envset.tmp" 2>nul
    del /f /q "%TEMP%\_envget.tmp" 2>nul

    :: Set these variables
    call "%TEMP%\_env.cmd"

    echo | set /p dummy="Done"
    echo .
55 голосов
/ 05 октября 2008

В Windows не предусмотрен встроенный механизм для распространения переменной среды add / change / remove в уже запущенный cmd.exe, либо из другого cmd.exe, либо из «Моего компьютера». -> Свойства -> Расширенные настройки -> Переменные среды ".

Если вы изменяете или добавляете новую переменную среды вне области существующей открытой командной строки, вам необходимо либо перезапустить командную строку, либо добавить вручную, используя SET в существующей командной строке.

Последний принятый ответ показывает частичное решение проблемы путем ручного обновления всех переменных среды в скрипте. Сценарий обрабатывает сценарий изменения глобальных переменных среды в «Мой компьютер ... Переменные среды», но если переменная среды изменяется в одном cmd.exe, сценарий не распространяет его на другой запущенный cmd.exe.

47 голосов
/ 28 июня 2017

В Windows 7/8/10 вы можете установить Chocolatey, в котором есть встроенный скрипт.

После установки Chocolatey просто введите «refreshenv» без кавычек.

34 голосов
/ 26 января 2012

Это работает на Windows 7: SET PATH=%PATH%;C:\CmdShortcuts

проверено, набрав echo% PATH%, и все заработало, хорошо. также установите, если вы открываете новый cmd, вам больше не нужны эти надоедливые перезагрузки:)

31 голосов
/ 26 февраля 2014

Я наткнулся на этот ответ, прежде чем в конце концов нашел более простое решение.

Просто перезапустите explorer.exe в диспетчере задач.

Я не тестировал, но вам также может потребоваться повторно открыть командную строку.

Кредит Тимо Хуовинен здесь: Узел не распознан, хотя успешно установлен (если это помогло вам, перейдите, пожалуйста, дайте кредит этого человека).

25 голосов
/ 14 августа 2012

Используйте "setx" и перезапустите командную строку

Для этой работы существует инструмент командной строки с именем " setx ". Это для чтения и записи переменных env. Переменные сохраняются после закрытия командного окна.

Он «Создает или изменяет переменные среды в пользовательской или системной среде, не требуя программирования или создания сценариев. Команда setx также извлекает значения ключей реестра и записывает их в текстовые файлы.»

Примечание: переменные, созданные или измененные этим инструментом, будут доступны в будущих окнах команд, но не в текущем окне команд CMD.exe. Итак, вам нужно перезагрузить.

Если setx отсутствует:


Или изменить реестр

MSDN говорит:

Для программного добавления или изменения системных переменных среды добавьте их к HKEY_LOCAL_MACHINE \ System \ CurrentControlSet \ Control \ сессии Раздел реестра Manager \ Environment , а затем WM_SETTINGCHANGE сообщение с lParam , установленным в строку " Environment ".

Это позволяет приложениям, таким как оболочка, забирать ваши обновления.

13 голосов
/ 19 марта 2010

вызов этой функции работал для меня:

VOID Win32ForceSettingsChange()
{
    DWORD dwReturnValue;
    ::SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, (LPARAM) "Environment", SMTO_ABORTIFHUNG, 5000, &dwReturnValue);
}
11 голосов
/ 22 июня 2012

Лучший способ, который я придумал, это просто сделать запрос в реестре. Вот мой пример.

В моем примере я выполнил установку с использованием пакетного файла, в который были добавлены новые переменные среды. Мне нужно было что-то сделать с этим, как только установка была завершена, но я не смог запустить новый процесс с этими новыми переменными. Я протестировал создание другого окна обозревателя и перезвонил cmd.exe, и это сработало, но в Vista и Windows 7 проводник запускается только как один экземпляр и обычно как пользователь, вошедший в систему. Это не получится при автоматизации, так как мне нужны права администратора для делать вещи независимо от запуска из локальной системы или от имени администратора на поле. Ограничением является то, что он не обрабатывает такие вещи, как путь, это работает только для простых переменных окружения. Это позволило мне использовать пакет для перехода в каталог (с пробелами) и копирования в файлы, запускаемые .exes и т. Д. Это было написано сегодня из майских ресурсов на stackoverflow.com

Оригинальные пакетные вызовы для новой партии:

testenvget.cmd SDROOT (или любая другая переменная)

@ECHO OFF
setlocal ENABLEEXTENSIONS
set keyname=HKLM\System\CurrentControlSet\Control\Session Manager\Environment
set value=%1
SET ERRKEY=0

REG QUERY "%KEYNAME%" /v "%VALUE%" 2>NUL| FIND /I "%VALUE%"
IF %ERRORLEVEL% EQU 0 (
ECHO The Registry Key Exists 
) ELSE (
SET ERRKEY=1
Echo The Registry Key Does not Exist
)

Echo %ERRKEY%
IF %ERRKEY% EQU 1 GOTO :ERROR

FOR /F "tokens=1-7" %%A IN ('REG QUERY "%KEYNAME%" /v "%VALUE%" 2^>NUL^| FIND /I "%VALUE%"') DO (
ECHO %%A
ECHO %%B
ECHO %%C
ECHO %%D
ECHO %%E
ECHO %%F
ECHO %%G
SET ValueName=%%A
SET ValueType=%%B
SET C1=%%C
SET C2=%%D
SET C3=%%E
SET C4=%%F
SET C5=%%G
)

SET VALUE1=%C1% %C2% %C3% %C4% %C5%
echo The Value of %VALUE% is %C1% %C2% %C3% %C4% %C5%
cd /d "%VALUE1%"
pause
REM **RUN Extra Commands here**
GOTO :EOF

:ERROR
Echo The the Enviroment Variable does not exist.
pause
GOTO :EOF

Также есть другой метод, который я придумал из разных идей. Пожалуйста, смотрите ниже. В основном это приведет к получению самой новой переменной пути из реестра, однако это вызовет ряд проблем, поскольку запрос реестра будет давать переменные сам по себе, что означает, что везде, где есть переменная, это не будет работать, поэтому для борьбы с этой проблемой я в основном удвоить путь. Очень противный Более совершенный метод будет делать: Установить путь =% Path%; C: \ Program Files \ Software .... \

Независимо от того, что это новый командный файл, пожалуйста, будьте осторожны.

@ECHO OFF
SETLOCAL ENABLEEXTENSIONS
set org=%PATH%
for /f "tokens=2*" %%A in ('REG QUERY "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" /v Path ^|FIND /I "Path"') DO (
SET path=%%B
)
SET PATH=%org%;%PATH%
set path
7 голосов
/ 15 августа 2013

Это можно сделать, переписав Таблицу среды внутри самого указанного процесса.

В качестве подтверждения концепции я написал этот пример приложения, которое только что отредактировало одну (известную) переменную среды в процессе cmd.exe:

typedef DWORD (__stdcall *NtQueryInformationProcessPtr)(HANDLE, DWORD, PVOID, ULONG, PULONG);

int __cdecl main(int argc, char* argv[])
{
    HMODULE hNtDll = GetModuleHandleA("ntdll.dll");
    NtQueryInformationProcessPtr NtQueryInformationProcess = (NtQueryInformationProcessPtr)GetProcAddress(hNtDll, "NtQueryInformationProcess");

    int processId = atoi(argv[1]);
    printf("Target PID: %u\n", processId);

    // open the process with read+write access
    HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION, 0, processId);
    if(hProcess == NULL)
    {
        printf("Error opening process (%u)\n", GetLastError());
        return 0;
    }

    // find the location of the PEB
    PROCESS_BASIC_INFORMATION pbi = {0};
    NTSTATUS status = NtQueryInformationProcess(hProcess, ProcessBasicInformation, &pbi, sizeof(pbi), NULL);
    if(status != 0)
    {
        printf("Error ProcessBasicInformation (0x%8X)\n", status);
    }
    printf("PEB: %p\n", pbi.PebBaseAddress);

    // find the process parameters
    char *processParamsOffset = (char*)pbi.PebBaseAddress + 0x20; // hard coded offset for x64 apps
    char *processParameters = NULL;
    if(ReadProcessMemory(hProcess, processParamsOffset, &processParameters, sizeof(processParameters), NULL))
    {
        printf("UserProcessParameters: %p\n", processParameters);
    }
    else
    {
        printf("Error ReadProcessMemory (%u)\n", GetLastError());
    }

    // find the address to the environment table
    char *environmentOffset = processParameters + 0x80; // hard coded offset for x64 apps
    char *environment = NULL;
    ReadProcessMemory(hProcess, environmentOffset, &environment, sizeof(environment), NULL);
    printf("environment: %p\n", environment);

    // copy the environment table into our own memory for scanning
    wchar_t *localEnvBlock = new wchar_t[64*1024];
    ReadProcessMemory(hProcess, environment, localEnvBlock, sizeof(wchar_t)*64*1024, NULL);

    // find the variable to edit
    wchar_t *found = NULL;
    wchar_t *varOffset = localEnvBlock;
    while(varOffset < localEnvBlock + 64*1024)
    {
        if(varOffset[0] == '\0')
        {
            // we reached the end
            break;
        }
        if(wcsncmp(varOffset, L"ENVTEST=", 8) == 0)
        {
            found = varOffset;
            break;
        }
        varOffset += wcslen(varOffset)+1;
    }

    // check to see if we found one
    if(found)
    {
        size_t offset = (found - localEnvBlock) * sizeof(wchar_t);
        printf("Offset: %Iu\n", offset);

        // write a new version (if the size of the value changes then we have to rewrite the entire block)
        if(!WriteProcessMemory(hProcess, environment + offset, L"ENVTEST=def", 12*sizeof(wchar_t), NULL))
        {
            printf("Error WriteProcessMemory (%u)\n", GetLastError());
        }
    }

    // cleanup
    delete[] localEnvBlock;
    CloseHandle(hProcess);

    return 0;
}

Пример вывода:

>set ENVTEST=abc

>cppTest.exe 13796
Target PID: 13796
PEB: 000007FFFFFD3000
UserProcessParameters: 00000000004B2F30
environment: 000000000052E700
Offset: 1528

>set ENVTEST
ENVTEST=def

Примечания

Этот подход также будет ограничен ограничениями безопасности. Если цель запускается на более высоком уровне или с более высокой учетной записью (например, SYSTEM), у нас не будет разрешения редактировать ее память.

Если вы хотите сделать это для 32-битного приложения, жестко закодированные смещения выше изменится на 0x10 и 0x48 соответственно. Эти смещения можно найти, выгрузив структуры _PEB и _RTL_USER_PROCESS_PARAMETERS в отладчике (например, в WinDbg dt _PEB и dt _RTL_USER_PROCESS_PARAMETERS)

Чтобы преобразовать концептуальное доказательство в то, что нужно ОП, он просто перечислит текущие переменные среды системы и пользователя (например, задокументированные в ответе @ tsadok) и запишет всю таблицу окружения в память целевого процесса. .

Редактировать: Размер блока среды также сохраняется в структуре _RTL_USER_PROCESS_PARAMETERS, но память выделяется в куче процесса. Поэтому из внешнего процесса у нас не было бы возможности изменить его размер и сделать его больше. Я поиграл с использованием VirtualAllocEx для выделения дополнительной памяти в целевом процессе для хранилища среды, и смог установить и прочитать совершенно новую таблицу. К сожалению, любая попытка изменить окружение из нормальных средств приведет к сбою и сгоранию, поскольку адрес больше не указывает на кучу (в RtlSizeHeap происходит сбой).

...