Есть ли команда для обновления переменных среды из командной строки в Windows?

481

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

Эрик Шуновер
источник
38
Фактически, каждая программа, которая должна их видеть, должна быть перезапущена. При запуске среда копируется в память процесса и, следовательно, больше не связана с определенными системой средами.
Джои
15
Прочитав их, я понял, что ложки нет ;) в реальном мире вы просто перезапустите cmd.
n611x007
Не команда, так что не совсем ответ, но есть поддержка для него с использованием Win32 API, если я правильно прочитал следующее: support.microsoft.com/en-us/help/104011/… Может быть возможность скомпилировать эту строку в простую программу на C и запускайте ее после обновления переменных среды.
Чарльз Грюнвальд
WM_SETTINGCHANGE (API-интерфейс win32, упомянутый @CharlesGrunwald) не работает для окон cmd.exe в соответствии с этой веткой: github.com/chocolatey/choco/issues/1589 - это причина, по которой они написали команду
refreshenv

Ответы:

137

Вы можете перехватить системные переменные окружения с помощью скрипта 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


Апологетика :

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

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

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

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

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

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

Это, вероятно, может быть улучшено.

ADDED

Если вам нужно экспортировать среду из одного окна 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"
itsadok
источник
2
Возможно, вы можете избежать временного файла, используя конструкцию FOR / F "tokens = 1, *" %% c IN ('resetvars.vbs') DO
tzot
2
Как я уже сказал в своем ответе «или, вручную добавьте, используя SET в существующей командной строке». это то, что это эффективно делает. Хороший ответ, хотя.
Кев
2
@itsadok - учитывая, что теперь это принятый ответ, вы должны добавить краткое объяснение в начале, чтобы поместить сценарий в контекст. т.е. указать, что невозможно распространить изменение env var на открытый cmd.exe без обновления вручную, как описано выше, или путем перезапуска cmd.exe.
Кев
Сценарий обрабатывает сценарий изменения глобальных переменных среды в «Мой компьютер ... Переменные среды», но если переменная среды изменяется в одном cmd.exe, сценарий не распространяет его на другой запущенный cmd.exe, который, я думаю, Вероятно, общий сценарий.
Кев
1
@Keyslinger: На самом деле это невозможно. Любая порожденная программа может обновлять свою собственную среду, но не среду запущенного экземпляра cmd.exe. Пакетный файл МОЖЕТ обновить среду cmd.exe, поскольку он запускается в том же экземпляре cmd.exe.
Бен Фойгт
112

Вот что использует 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 .
анонимный трус
источник
66
+1 Если у вас установлен Chocolatey, вы можете просто запустить RefreshEnvобновленные переменные среды в текущем сеансе.
Мартин Валгур,
2
Это невероятно полезная часть служебного программного обеспечения, большое спасибо за обмен.
Сабунку
10
Примечание: Chocolatey перенесла репозитории, и последнюю версию этого скрипта можно найти здесь (с некоторыми исправлениями ошибок): github.com/chocolatey/choco/blob/master/src/…
Michael Burr
1
это должно работать Powershellтоже? Это только кажется, что работает cmd.exeдля меня.
Craq
1
Это работает для меня в PowerShell, @craq. Под управлением Windows10 x64.
мазунки
100

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

После установки Chocolatey просто введите refreshenv.

веселый
источник
это правильный ответ, было бы приятно услышать от парня, который проголосовал против
the_joric
2
Что я делаю неправильно? $> refreshenv 'refreshenv' не распознается как внутренняя или внешняя команда, работающая программа или пакетный файл.
aclokay
@aclokay Не уверен. Пожалуйста, предоставьте более подробную информацию о вашей системе conf для отладки. Между тем, вы можете сослаться на аналогичный открытый вопрос здесь. github.com/chocolatey/choco/issues/250
веселый
Это не работает для меня тоже. Я работаю на W7 professional, возможно, он работает только в более полных версиях.
alseether
1
Если refreshenv преждевременно существует в вашем скрипте (как это было для меня), вы можете использовать "call RefreshEnv.cmd" вместо этого. (см. github.com/chocolatey/choco/issues/1461 )
sfiss
59

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

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

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

Кев
источник
Если у кого-то есть рабочее решение этой проблемы, я периодически проверяю и могу изменить принятый ответ.
Эрик Шуновер
1
Это не должен быть принятый ответ просто потому, что он не отвечает на заданный вопрос. этот вопрос должен оставаться без принятого ответа до тех пор, пока он не будет найден.
Shoosh
4
И досадно, что дополнительные экземпляры cmd.exe не учитываются. Они все должны быть убиты до того , как изменение будет отражено в любых новых cmd.exe лет.
6
Отрицательные комментарии и отметка «вниз» этого ответа показывают, как иногда происходит переполнение битого стека. Кев дал правильный ответ. Просто потому, что вам не нравится, это не причина, чтобы отметить это.
Дэвид Арно
Кев определенно отвечает на вопрос. Вопрос в том, нет встроенного решения.
Эрик Шуновер
40

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

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

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

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

wharding28
источник
Я приехал сюда, потому что пытался добавить внешний инструмент в Visual Studio, чтобы я мог открыть командную строку в корне моего решения, как описано в этом блоге: neverindoubtnet.blogspot.com/2012/10/… ... и У меня были похожие проблемы ... Я пытался заставить "git" появляться в моей переменной пути. Я добавил каталоги git в переменную PATH, но тогда они не будут отображаться в командной строке, которую я открою из Visual Studio. Простым решением было перезапустить Visual Studio. Тогда новые дополнения к переменной PATH были видны в cmd.
Дэвид Барроуз
4
Это решение помогает мне в Windows 10
ganchito55
7
Вопрос был: «Могу ли я выполнить команду, которая бы делала это без перезапуска CMD
Флориан Ф
Хорошо, из диспетчера задач я не смог перезапустить explorer.exe, только закончить его. Я сделал это, но моя панель задач была сломана. Для начала explorer; exe это действительно просто. Давайте «Ctrl + shift + escape» -> file -> «выполнить новое задание» -> «explorer.exe» сделал всю работу за меня. И да, ведь env var был использован в новом окне cmd. Спасибо за все
Оскар
Хорошее решение, спасибо! Чтобы развернуть и обратиться к комментарию @Oscar: Запустите cmdокно от имени Администратора. Используйте команду taskkill /f /im explorer.exe && explorer.exe. Это убьет процесс explorer.exe и перезапустит его.
S3DEV
32

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

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

Кристофер Монссон
источник
1
Не работает для "нового cmd" для меня (Win7 x64). Смотрите скринвидео
Игорь
26
Это не решает вопрос, задаваемый, и не должен. Первоначальный вопрос заключается в том, как обновить переменную среды до значения, которое было установлено вне этого терминала.
csauve
Хотя это не отвечает на вопрос, оно обеспечивает половину лучшего рабочего решения. Я использую это для любой устанавливаемой переменной, затем открываю панель управления и глобально добавляю переменную среды. Я не люблю использовать, setxпотому что он наследует текущую среду, которая может иметь переменные, которые были изменены, а не то, что я хочу навсегда. Делая это таким образом, я могу избежать перезапуска консоли, чтобы использовать переменные, и в то же время избежать проблемы, когда они не будут доступны глобально в будущем.
dgo
25

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

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

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

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

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


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

MSDN говорит:

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

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

Йенс А. Кох
источник
1
Не могли бы вы рассказать о том, как использовать setx для чтения переменных среды? Я изучил различные документы и просто не вижу их. : - /
Марк Рибау
2
setx VARIABLE -k "HKEY_LOCAL_MACHINE \ Software \ Microsoft \ WindowsNT \ CurrentVersion \ CurrentVersion" echo% VARIABLE%
Йенс А. Кох
3
текущая системная среда: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment\VARIABLEтекущая пользовательская среда: HKEY_CURRENT_USER\Environment\VARIABLE
Марк Рибау
5
setx /? в примечании говорится: «В локальной системе переменные, созданные или измененные этим инструментом, будут доступны в будущих командных окнах, но не в текущем командном окне CMD.exe». ОП хотел обновить текущий cmd.
Superole
4
Покупатель, будь осторожен! Если у вас особенно длинная длина, %PATH%то setxможете сократить это до 1024 байтов! И вот так, его вечер исчез
FaCE
15

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

VOID Win32ForceSettingsChange()
{
    DWORD dwReturnValue;
    ::SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, (LPARAM) "Environment", SMTO_ABORTIFHUNG, 5000, &dwReturnValue);
}
Брайан Виид
источник
8
и не все программы слушают это сообщение (на самом деле большинство из них, вероятно, не слушают)
Rehan Khwaja
Нет, это также работает для не-GUI программ. Что касается прослушивания программ ... проблема, если гарантируется, что перезапущенная программа получит обновленную среду, и это дает вам это.
user2023370 14.02.15
11

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

В моем примере я сделал установку с использованием пакетного файла, в который были добавлены новые переменные среды. Мне нужно было что-то сделать, как только установка была завершена, но я не смог запустить новый процесс с этими новыми переменными. Я протестировал создание другого окна обозревателя и перезвонил 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

Также есть другой метод, который я придумал из разных идей. Пожалуйста, смотрите ниже. В основном это приведет к получению самой новой переменной пути из реестра, однако это вызовет ряд проблем, поскольку запрос реестра будет давать переменные сам по себе, что означает, что везде, где есть переменная, это не будет работать, поэтому для борьбы с этой проблемой я в основном удвоить путь. Очень противный Более совершенный метод должен был бы сделать: Set Path =% 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
Кристофер Холмс
источник
8

Самый простой способ добавить переменную в путь без перезагрузки для текущего сеанса - это открыть командную строку и ввести:

PATH=(VARIABLE);%path%

и нажмите enter.

чтобы проверить, загружена ли ваша переменная, введите

PATH

и нажмите enter. Тем не менее, переменная будет только частью пути до перезагрузки.

Ричард Вудрафф
источник
у меня не сработало, не удалось получить доступ к двоичным файлам в переменной пути
user2305193
7

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

В качестве подтверждения концепции я написал этот пример приложения, которое только что отредактировало одну (известную) переменную среды в процессе 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 )

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

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

Джош Поули
источник
6

Переменные среды хранятся в HKEY_LOCAL_MACHINE \ SYSTEM \ ControlSet \ Control \ Session Manager \ Environment.

Многие из полезных переменных env, такие как Path, хранятся как REG_SZ. Есть несколько способов получить доступ к реестру, включая REGEDIT:

REGEDIT /E &lt;filename&gt; "HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Session Manager\Environment"

Вывод начинается с магических чисел. Таким образом, чтобы найти его с помощью команды find, его нужно набрать и перенаправить:type <filename> | findstr -c:\"Path\"

Так что, если вы просто хотите обновить переменную пути в текущем сеансе команды с помощью того, что находится в системных свойствах, следующий пакетный скрипт работает нормально:

RefreshPath.cmd:

    @ эхо выключено

    REM Это решение запрашивает повышение прав для чтения из реестра.

    если существует% temp% \ env.reg del% temp% \ env.reg / q / f

    REGEDIT / E% temp% \ env.reg "HKEY_LOCAL_MACHINE \ SYSTEM \ ControlSet001 \ Control \ Диспетчер сеансов \ Среда"

    если не существует% temp% \ env.reg (
       echo "Невозможно записать реестр во временную папку"
       выход 1
       )

    SETLOCAL EnableDelayedExpansion

    for / f "токены = 1,2 * delims ==" %% i in ('type% temp% \ env.reg ^ | findstr -c: \ "Path \" =') do (
       set upath = %% ~ j
       эхо! upath: \\ = \! >% Temp% \ NewPath
       )

     ENDLOCAL

     for / f "tokens = *" %% i в (% temp% \ newpath) установить путь = %% i
Algonaut
источник
5
Переменные среды не хранятся в реестре. В реестре хранится шаблон , из которого такие программы, как Windows Explorer, (заново) создают свои переменные среды при получении соответствующего уведомления . Фактические переменные среды являются для каждого процесса и хранятся в собственном адресном пространстве каждого процесса, первоначально наследуемого от его родительского процесса и впоследствии модифицируемого по прихоти процесса.
JdeBP
5

Попробуйте открыть новую командную строку от имени администратора. Это работало для меня на Windows 10. (Я знаю, что это старый ответ, но я должен был поделиться этим, потому что писать сценарий VBS просто для этого абсурдно).

estebro
источник
5

Перезапуск проводника сделал это для меня, но только для новых терминалов cmd.

Терминал, который я установил, мог уже видеть новую переменную Path (в Windows 7).

taskkill /f /im explorer.exe && explorer.exe
Винс
источник
5

Смущает то, что есть несколько мест для запуска cmd. В моем случае я запустил cmd из проводника Windows, и переменные среды не изменились, а при запуске cmd из «запуска» (windows key + r) переменные среды были изменены .

В моем случае мне просто пришлось убить процесс проводника Windows из диспетчера задач, а затем снова запустить его из диспетчера задач .

После этого у меня был доступ к новой переменной среды из cmd, который был создан из проводника Windows.

Даниэль Фенстерхайм
источник
3

Я использую следующий код в своих пакетных скриптах:

if not defined MY_ENV_VAR (
    setx MY_ENV_VAR "VALUE" > nul
    set MY_ENV_VAR=VALUE
)
echo %MY_ENV_VAR%

Используя SET после SETX, можно напрямую использовать локальную переменную без перезапуска командного окна. И при следующем запуске будет использоваться переменная окружения.

Себастьян
источник
Пока я получаю то, что вы сделали, скорее всего, он хочет что-то для параллельных скриптов, один скрипт устанавливает глобальные переменные, а другой читает их. В противном случае, нет смысла привлекать setx, set будет достаточно.
JasonXA
3

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

Сделайте файл refreshEnv.batгде-нибудь на вашем PATH. Обновите вашу консольную среду, выполнив refreshEnv.

@ECHO OFF
REM Source found on https://github.com/DieterDePaepe/windows-scripts
REM Please share any improvements made!

REM Code inspired by http://stackoverflow.com/questions/171588/is-there-a-command-to-refresh-environment-variables-from-the-command-prompt-in-w

IF [%1]==[/?] GOTO :help
IF [%1]==[/help] GOTO :help
IF [%1]==[--help] GOTO :help
IF [%1]==[] GOTO :main

ECHO Unknown command: %1
EXIT /b 1 

:help
ECHO Refresh the environment variables in the console.
ECHO.
ECHO   refreshEnv       Refresh all environment variables.
ECHO   refreshEnv /?        Display this help.
GOTO :EOF

:main
REM Because the environment variables may refer to other variables, we need a 2-step approach.
REM One option is to use delayed variable evaluation, but this forces use of SETLOCAL and
REM may pose problems for files with an '!' in the name.
REM The option used here is to create a temporary batch file that will define all the variables.

REM Check to make sure we don't overwrite an actual file.
IF EXIST %TEMP%\__refreshEnvironment.bat (
  ECHO Environment refresh failed!
  ECHO.
  ECHO This script uses a temporary file "%TEMP%\__refreshEnvironment.bat", which already exists. The script was aborted in order to prevent accidental data loss. Delete this file to enable this script.
  EXIT /b 1
)

REM Read the system environment variables from the registry.
FOR /F "usebackq tokens=1,2,* skip=2" %%I IN (`REG QUERY "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment"`) DO (
  REM /I -> ignore casing, since PATH may also be called Path
  IF /I NOT [%%I]==[PATH] (
    ECHO SET %%I=%%K>>%TEMP%\__refreshEnvironment.bat
  )
)

REM Read the user environment variables from the registry.
FOR /F "usebackq tokens=1,2,* skip=2" %%I IN (`REG QUERY HKCU\Environment`) DO (
  REM /I -> ignore casing, since PATH may also be called Path
  IF /I NOT [%%I]==[PATH] (
    ECHO SET %%I=%%K>>%TEMP%\__refreshEnvironment.bat
  )
)

REM PATH is a special variable: it is automatically merged based on the values in the
REM system and user variables.
REM Read the PATH variable from the system and user environment variables.
FOR /F "usebackq tokens=1,2,* skip=2" %%I IN (`REG QUERY "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" /v PATH`) DO (
  ECHO SET PATH=%%K>>%TEMP%\__refreshEnvironment.bat
)
FOR /F "usebackq tokens=1,2,* skip=2" %%I IN (`REG QUERY HKCU\Environment /v PATH`) DO (
  ECHO SET PATH=%%PATH%%;%%K>>%TEMP%\__refreshEnvironment.bat
)

REM Load the variable definitions from our temporary file.
CALL %TEMP%\__refreshEnvironment.bat

REM Clean up after ourselves.
DEL /Q %TEMP%\__refreshEnvironment.bat

ECHO Environment successfully refreshed.
DieterDP
источник
это также для% CLIENTNAME%? - у меня не сработало - stackoverflow.com/questions/37550160/…
Игорь Л.
% CLIENTNAME% недоступен в моей среде, и, прочитав ваш вопрос, я предположу, что это что-то заданное внешним процессом. (Когда процесс запускает дочерний процесс, он может настроить среду для этого дочернего процесса.) Поскольку он не является частью реальных переменных среды, он не будет обновляться этим сценарием.
DieterDP
Привет @DieterDP, ваше решение работает для меня! Я использую Windows 10 на 64-битной машине. Я получаю сообщение об ошибке: «ОШИБКА: системе не удалось найти указанный раздел реестра или значение». Тем не менее, обновление переменных среды прошло успешно. Откуда берется ошибка?
К.Мюльер
Трудно сказать, фактически не проверяя это сам, но я предполагаю, что структура реестра на W10 может немного отличаться. Если вам это нравится, попробуйте найти ошибку, выполнив команды в командной строке.
DieterDP
2

Если это относится только к одной (или нескольким) конкретным переменным, которые вы хотите изменить, я думаю, что самый простой способ - это обходной путь : просто установить в своей среде И в текущем сеансе консоли

  • Set поместит переменную в ваш текущий сеанс
  • SetX поместит переменную в среду, но НЕ в текущую сессию

У меня есть этот простой пакетный скрипт для изменения моего Maven с Java7 на Java8 (которые оба являются env. Vars). Пакетная папка находится в моей переменной PATH, поэтому я всегда могу вызвать ' j8 ' и в своей консоли, и в среде мой JAVA_HOME var меняется:

j8.bat:

@echo off
set JAVA_HOME=%JAVA_HOME_8%
setx JAVA_HOME "%JAVA_HOME_8%"

До сих пор я считаю, что это работает лучше и проще. Вы, вероятно, хотите, чтобы это было в одной команде, но ее просто нет в Windows ...

Йерун ван Дейк-Джун
источник
2

Решение, которым я пользуюсь уже несколько лет:

@echo off
rem Refresh PATH from registry.
setlocal
set USR_PATH=
set SYS_PATH=
for /F "tokens=3* skip=2" %%P in ('%SystemRoot%\system32\reg.exe query "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" /v PATH') do @set "SYS_PATH=%%P %%Q"
for /F "tokens=3* skip=2" %%P in ('%SystemRoot%\system32\reg.exe query "HKCU\Environment" /v PATH') do @set "USR_PATH=%%P %%Q"
if "%SYS_PATH:~-1%"==" " set "SYS_PATH=%SYS_PATH:~0,-1%"
if "%USR_PATH:~-1%"==" " set "USR_PATH=%USR_PATH:~0,-1%"
endlocal & call set "PATH=%SYS_PATH%;%USR_PATH%"
goto :EOF

Изменить: Woops, вот обновленная версия.

Чарльз Грюнвальд
источник
Мне нравится твой ответ. Отправьте тот же ответ на мой вопрос здесь stackoverflow.com/q/61473551/1082063, и я приму его в качестве ответа. Спасибо.
Дэвид И. Макинтош
1

Прямого пути нет, как сказал Кев. В большинстве случаев проще создать другую коробку CMD. Более досадно, что запущенные программы также не знают об изменениях (хотя IIRC может быть широковещательным сообщением, которое нужно посмотреть, чтобы получить уведомление о таком изменении).

Хуже было: в старых версиях Windows вам приходилось выходить из системы, а затем снова входить в систему, чтобы учесть изменения ...

PhiLho
источник
1

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

#REQUIRES -Version 3.0

if (-not ("win32.nativemethods" -as [type])) {
    # import sendmessagetimeout from win32
    add-type -Namespace Win32 -Name NativeMethods -MemberDefinition @"
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern IntPtr SendMessageTimeout(
   IntPtr hWnd, uint Msg, UIntPtr wParam, string lParam,
   uint fuFlags, uint uTimeout, out UIntPtr lpdwResult);
"@
}

$HWND_BROADCAST = [intptr]0xffff;
$WM_SETTINGCHANGE = 0x1a;
$result = [uintptr]::zero

function global:ADD-PATH
{
    [Cmdletbinding()]
    param ( 
        [parameter(Mandatory=$True, ValueFromPipeline=$True, Position=0)] 
        [string] $Folder
    )

    # See if a folder variable has been supplied.
    if (!$Folder -or $Folder -eq "" -or $Folder -eq $null) { 
        throw 'No Folder Supplied. $ENV:PATH Unchanged'
    }

    # Get the current search path from the environment keys in the registry.
    $oldPath=$(Get-ItemProperty -Path 'Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment' -Name PATH).Path

    # See if the new Folder is already in the path.
    if ($oldPath | Select-String -SimpleMatch $Folder){ 
        return 'Folder already within $ENV:PATH' 
    }

    # Set the New Path and add the ; in front
    $newPath=$oldPath+';'+$Folder
    Set-ItemProperty -Path 'Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment' -Name PATH -Value $newPath -ErrorAction Stop

    # Show our results back to the world
    return 'This is the new PATH content: '+$newPath

    # notify all windows of environment block change
    [win32.nativemethods]::SendMessageTimeout($HWND_BROADCAST, $WM_SETTINGCHANGE, [uintptr]::Zero, "Environment", 2, 5000, [ref]$result)
}

function global:REMOVE-PATH {
    [Cmdletbinding()]
    param ( 
        [parameter(Mandatory=$True, ValueFromPipeline=$True, Position=0)]
        [String] $Folder
    )

    # See if a folder variable has been supplied.
    if (!$Folder -or $Folder -eq "" -or $Folder -eq $NULL) { 
        throw 'No Folder Supplied. $ENV:PATH Unchanged'
    }

    # add a leading ";" if missing
    if ($Folder[0] -ne ";") {
        $Folder = ";" + $Folder;
    }

    # Get the Current Search Path from the environment keys in the registry
    $newPath=$(Get-ItemProperty -Path 'Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment' -Name PATH).Path

    # Find the value to remove, replace it with $NULL. If it's not found, nothing will change and you get a message.
    if ($newPath -match [regex]::Escape($Folder)) { 
        $newPath=$newPath -replace [regex]::Escape($Folder),$NULL 
    } else { 
        return "The folder you mentioned does not exist in the PATH environment" 
    }

    # Update the Environment Path
    Set-ItemProperty -Path 'Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment' -Name PATH -Value $newPath -ErrorAction Stop

    # Show what we just did
    return 'This is the new PATH content: '+$newPath

    # notify all windows of environment block change
    [win32.nativemethods]::SendMessageTimeout($HWND_BROADCAST, $WM_SETTINGCHANGE, [uintptr]::Zero, "Environment", 2, 5000, [ref]$result)
}


# Use ADD-PATH or REMOVE-PATH accordingly.

#Anything to Add?

#Anything to Remove?

REMOVE-PATH "%_installpath_bin%"
Юлиан Дита
источник
1

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

Например, мы используем это, чтобы разрешить развертывание и настройку программного обеспечения на большом количестве машин, которые мы регулярно переустанавливаем. И я должен признать, что перезапуск командной строки во время развертывания нашего программного обеспечения был бы очень непрактичным и потребовал бы от нас поиска обходных путей, которые не обязательно были бы приятными. Давайте вернемся к нашей проблеме. Мы действуем следующим образом.

1 - у нас есть пакетный скрипт, который в свою очередь вызывает скрипт powershell, подобный этому

[файл: task.cmd] .

CMD > powershell.exe -executionpolicy unrestricted -File C:\path_here\refresh.ps1

2 - После этого скрипт refresh.ps1 обновляет переменные среды, используя ключи реестра (GetValueNames () и т. Д.). Затем в том же скрипте powershell нам просто нужно вызвать новые переменные среды. Например, в типичном случае, если мы только что установили nodeJS ранее с помощью cmd с помощью тихих команд, после вызова функции мы можем напрямую вызвать npm для установки в том же сеансе определенных пакетов, как показано ниже.

[файл: refresh.ps1]

function Update-Environment {
    $locations = 'HKLM:\SYSTEM\CurrentControlSet\Control\Session  Manager\Environment',
                 'HKCU:\Environment'
    $locations | ForEach-Object {
        $k = Get-Item $_
        $k.GetValueNames() | ForEach-Object {
            $name  = $_
            $value = $k.GetValue($_)

            if ($userLocation -and $name -ieq 'PATH') {
                $env:Path += ";$value"
            } else {

                Set-Item -Path Env:\$name -Value $value
            }
        }
        $userLocation = $true
    }
}
Update-Environment
#Here we can use newly added environment variables like for example npm install.. 
npm install -g create-react-app serve

После завершения сценария powershell сценарий cmd выполняет другие задачи. Теперь следует иметь в виду, что после завершения задачи cmd по-прежнему не имеет доступа к новым переменным среды, даже если сценарий powershell обновил их в своем собственном сеансе. Вот почему мы выполняем все необходимые задачи в скрипте powershell, который, конечно, может вызывать те же команды, что и cmd.

Энди МакРей
источник
0

Изменить: это работает, только если изменения среды, которые вы делаете, являются результатом запуска командного файла.

Если пакетный файл начинается с SETLOCALэтого, он всегда будет возвращаться к исходной среде при выходе, даже если вы забудете позвонитьENDLOCAL до выхода из пакета или если он неожиданно прервется.

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

ENDLOCAL & (
  SET RESULT1=%RESULT1%
  SET RESULT2=%RESULT2%
)
wardies
источник
-1

Чтобы решить эту проблему, я изменил переменную среды, используя ОБА setx и set, а затем перезапустил все экземпляры explorer.exe. Таким образом, любой запущенный процесс будет иметь новую переменную среды.

Мой пакетный скрипт для этого:

setx /M ENVVAR "NEWVALUE"
set ENVVAR="NEWVALUE"

taskkill /f /IM explorer.exe
start explorer.exe >nul
exit

Проблема с этим подходом состоит в том, что все окна проводника, которые в данный момент открыты, будут закрыты, что, вероятно, является плохой идеей. Но посмотрите статью Кева, чтобы узнать, почему это необходимо

Йенс Хиккельбьерг
источник