Чи є команда для оновлення змінних оточення з командного рядка в Windows?


480

Якщо я змінюю або додаю змінну середовища, мені доведеться перезапустити командний рядок. Чи є команда, яку я міг би виконати, що зробить це без перезавантаження CMD?


38
Власне, кожну програму, яка потребує їх перегляду, потрібно перезапустити. Навколишнє середовище копіюється в пам'ять процесу при запуску і тому більше не має жодного зв'язку з визначеними системою envvars.
Joey

15
прочитавши їх, я зрозумів, що ложки немає ;) у реальному світі ти просто перезапустиш cmd.
n611x007

Не команда, тож не зовсім відповідь, але є підтримка для неї за допомогою API Win32, якщо я правильно прочитав таке: support.microsoft.com/en-us/help/104011/… Ви мали б змогу компілювати цей рядок у проста програма C і запускайте її, слід за оновленнями змінної середовища.
Чарльз Грюнвальд,

WM_SETTINGCHANGE (api win32, згаданий @CharlesGrunwald) не працює для cmd.exe Windows відповідно до цієї теми : github.com/chocolatey/choco/isissue/1589 - це причина, коли вони написали команду refreshenv
davr

Відповіді:


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.

Примітка : цей скрипт не видаляє змінні.

Можливо, це можна покращити.

ДОБАВЛЕНО

Якщо вам потрібно експортувати середовище з одного вікна 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"

2
Можливо, ви можете уникнути тимчасового файлу, використовуючи FOR / F "лексеми = 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 .

65
+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 Не впевнений. Будь ласка, надайте більш детальну інформацію про конфіденційність системи ур. Тим часом ви можете звернутися до подібного відкритого питання тут. github.com/chocolatey/choco/isissue/250
веселий

Не працює і для мене. Я на W7 professional, можливо, він працює лише в більш повних версіях.
alseether

1
Якщо refreshenv передчасно існує ваш сценарій (як це було для мене), ви можете використовувати "call RefreshEnv.cmd" замість цього. (див. github.com/chocolatey/choco/isissue/1461 )
sfiss

59

За задумом немає вбудованого механізму для Windows для поширення змінної середовища додавання / зміна / видалення до вже запущеного cmd.exe, або з іншого cmd.exe, або з "Мій комп'ютер -> Властивості -> Розширені налаштування -> Змінні середовища ".

Якщо ви змінюєте або додаєте нову змінну середовища за межами області існуючої відкритої командної лінії, вам потрібно буде або перезапустити командний рядок, або вручну додати за допомогою SET у існуючий командний рядок.

В останні загальноприйнятий відповідь показує часткове обхідним шляхом вручну оновити всі змінні оточення в скрипті. Сценарій обробляє випадок використання змінних змінних середовищ у всьому світі в "Мій комп'ютер ... Змінні середовища", але якщо змінна середовища змінена в одному cmd.exe, скрипт не поширюватиме його на інший запущений cmd.exe.


Якщо у когось є робоче рішення цієї проблеми, я періодично перевірятиму, і я можу змінити прийняту відповідь.
Ерік Шкуновер

1
Це не має бути прийнятою відповіддю просто тому, що вона не відповідає на поставлене запитання. це питання має залишатися без прийнятої відповіді, поки не знайдеться.
shoosh

4
І прикро, зайві екземпляри cmd.exe не враховуються. Вони все повинні бути вбиті до того , як зміна буде відображено в будь-яких нових cmd.exe років.

6
Негативні коментарі та позначення цієї відповіді вказують на те, наскільки зламаним є переповнення стека. Кев дав правильну відповідь. Тільки тому, що вам це не подобається, це не привід відзначати це.
Девід Арно

Кев однозначно відповідає на питання. Питання в тому, що немає вбудованого рішення.
Ерік Шкоуновер

40

Я натрапив на цю відповідь, перш ніж врешті-решт знайти більш просте рішення.

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

Я не тестував, але вам може знадобитися знову відкрити командний рядок.

Кредит Тимо Хуовінену тут: Вузол не розпізнаний, хоча він успішно встановлений (якщо це допомогло вам, будь ласка, відправте коментар цього чоловіка)


Я приїхав сюди, тому що намагався додати зовнішній інструмент до візуальної студії, щоб я міг відкрити командний рядок у корені свого рішення, як описано в цьому блозі: neverindoubtnet.blogspot.com/2012/10/… ... і У мене були подібні проблеми ... Я намагався отримати "git", щоб відобразитися в моїй змінній шляху. Я додав каталоги git до змінної PATH, але тоді вони не з'являться у командному рядку, що я відкрию з Visual Studio. Простим рішенням було перезапустити Visual Studio. Тоді нові доповнення до змінної PATH були помітні в cmd.
Девід Барроуз

4
Це рішення допомагає мені в Windows 10
ganchito55

7
Питання було таке: "Чи є команда, яку я міг би виконати, що зробить це без перезавантаження CMD ?"
Флоріан Ф

Гаразд із диспетчера завдань я не зміг перезапустити explorer.exe, лише закінчив його. Я це зробив, але мою панель завдань було порушено. Запустити провідник; exe - це дуже просто. Давайте "Ctrl + shift + escape" -> файл -> "виконати нове завдання" -> "explorer.exe" зробив для мене роботу. І так, адже env var був використаний у новому вікні cmd. Спасибі за все
Оскар

Гарне рішення, дякую! Для розширення та звернення до коментаря @Oscar: Створіть cmdвікно як адміністратор. Використовуйте команду taskkill /f /im explorer.exe && explorer.exe. Це вб'є процес explor.exe і перезапустить його.
S3DEV

32

Це працює на Windows 7: SET PATH=%PATH%;C:\CmdShortcuts

перевірено, набравши echo% PATH%, і воно спрацювало, добре. також встановіть, якщо ви відкриєте новий cmd, більше не потрібно в цих примхливих перезавантаженнях :)


1
Не працює для "нового cmd" для мене (Win7 x64). Дивіться screenvideo
Ігор

26
Це не вирішує поставлене питання, а також не повинно. Первісне питання полягає в тому, як оновити змінну середовища до значення, яке було встановлено поза цим терміналом.
csauve

Хоча це не дає відповіді на питання, воно дає половину найкращого робочого рішення. Я використовую це - для будь-якої змінної, яку я встановлюю - тоді я відкриваю панель управління і додаю змінну навколишнього середовища в усьому світі. Мені не подобається використовувати, setxоскільки він успадковує поточне середовище, яке може мати змінені зміни, а не те, що я хочу постійно. Це дозволяє мені уникнути перезавантаження консолі з метою використання змінних, уникаючи при цьому проблеми їх недоступності у всьому світі.
дго

25

Використовуйте "setx" та перезапустіть cmd-рядок

Для цього завдання існує інструмент командного рядка з назвою " 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" відлунюється% VARIABLE%
Jens A. Koch

3
поточне системне середовище: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment\VARIABLEпоточне середовище користувача: HKEY_CURRENT_USER\Environment\VARIABLE
Марк Рибау

5
setx /? в примітці йдеться: "У локальній системі змінні, створені або модифіковані цим інструментом, будуть доступні у майбутніх вікнах команд, але не у поточному вікні команд CMD.exe." OP хотів оновити поточний cmd.
Superole

4
Покупця, будьте обачні! Якщо у вас особливо довгий, %PATH%то setxможете скоротити це до 1024 байтів! І просто так, його вечір зник
липня

15

Виклик цієї функції працював для мене:

VOID Win32ForceSettingsChange()
{
    DWORD dwReturnValue;
    ::SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, (LPARAM) "Environment", SMTO_ABORTIFHUNG, 5000, &dwReturnValue);
}

8
і не всі програми слухають це повідомлення (насправді більшість із них, мабуть, не так)
Rehan Khwaja

Ні, він також працює для програм, що не є графічним інтерфейсом. Що стосується прослуховування програм ... проблема, якщо гарантувати, що перезапущена програма отримає оновлене середовище, і це дає вам це.
користувач2023370

11

Найкращий метод, який я придумав, - це просто зробити запит у реєстрі. Ось мій приклад.

У моєму прикладі я зробив установку за допомогою файлу Batch, який додав нові змінні середовища. Мені потрібно було вчинити з цим, як тільки установка завершилася, але не вдалося породити новий процес цими новими змінними. Я перевірив нерест ще одного вікна провідника і повернувся до cmd.exe, і це спрацювало, але на Vista та Windows 7, Провідник працює лише як один екземпляр і, як правило, людина, яка ввійшла в систему. Це не вдасться до автоматизації, оскільки мені потрібні мої кредити адміністратора, щоб робити речі незалежно від запуску з локальної системи або як адміністратора на коробці. Обмеженням цього є те, що він не обробляє такі речі, як шлях, це працювало лише на простих змінних оточення. Це дозволило мені використовувати пакет, щоб перейти до каталогу (з пробілами) та скопіювати у файли запуск .exes тощо. Це було написано сьогодні з ресурсів травня на stackoverflow.com

Orginal Batch викликає нову партію:

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

Також є ще один метод, який я придумав з різних різних ідей. Дивіться нижче. Це в основному отримає нову змінну шляху з реєстру, однак це призведе до ряду питань, оскільки запит реєстру збирається давати самі змінні, це означає, що всюди є змінна, яка не працюватиме, тому для боротьби з цією проблемою я в основному подвоїти шлях. Дуже бридкий. Більш досконалим методом було б зробити: Встановити Шлях =% Шлях%; C: \ Програмні файли \ Програмне забезпечення .... \

Незалежно від того, тут є новий пакетний файл, будьте обережні.

@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)

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

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


6

Змінні середовища зберігаються у HKEY_LOCAL_MACHINE \ SYSTEM \ ControlSet \ Control \ Session Manager \ Environment.

Багато корисних середовищ, таких як 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:

    @echo off

    REM Це рішення вимагає підвищення, щоб прочитати з реєстру.

    якщо існує% temp% \ env.reg del% temp% \ env.reg / q / f

    REGEDIT / E% temp% \ env.reg "HKEY_LOCAL_MACHINE \ SYSTEM \ ControlSet001 \ Control \ Session Manager \ Environment"

    якщо не існує% temp% \ env.reg (
       echo "Неможливо записати реєстр у тимчасове розташування"
       вихід 1
       )

    SETLOCAL EnableDelayedExpansion

    для / f "tokens = 1,2 * delims ==" %% i in ('введіть% temp% \ env.reg ^ | findstr -c: \ "Шлях \" =') виконайте (
       set upath = %% ~ j
       відлуння! вгору: \\ = \! >% temp% \ newpath
       )

     ENDLOCAL

     для / f "tokens = *" %% i в (% temp% \ newpath) встановити шлях = %% i

5
Змінні середовища не зберігаються в реєстрі. Що міститься в реєстрі, це шаблон , з якого такі програми, як Windows Explorer (повторно) будують свої змінні середовища, коли їх повідомляють про це . Фактичні змінні середовища є кожним процесом і зберігаються у власному адресному просторі кожного процесу, спочатку успадковуються від його батьківського процесу та змінюються згодом за примхою процесу.
JdeBP

5

Спробуйте відкрити новий командний рядок як адміністратор. Це працювало для мене в Windows 10. (Я знаю, що це стара відповідь, але мені довелося поділитися цим, оскільки писати сценарій VBS саме для цього є абсурдом).


5

Перезапуск дослідника зробив це для мене, але лише для нових терміналів cmd.

Термінал, який я встановив шлях, вже міг бачити нову змінну Path (у Windows 7).

taskkill /f /im explorer.exe && explorer.exe

5

Неприємним може бути те, що є кілька місць, з яких можна починати cmd. У моєму випадку я запустив cmd від windows Explorer, і змінні середовища не змінилися, коли при запуску cmd з "run" (клавіша Windows + r) змінені середовища були змінені .

У моєму випадку мені просто довелося вбити процес Windows Explorer з диспетчера завдань, а потім знову запустити його з менеджера завдань .

Як тільки я це зробив, я отримав доступ до нової змінної середовища з cmd, що був породжений Windows Explorer.


3

Я використовую такий код у своїх пакетних сценаріях:

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

Використовуючи SET після SETX , можна використовувати змінну "local" безпосередньо без перезавантаження вікна команд. І при наступному запуску буде використана змінна середовище.


Хоча я отримую те, що ви зробили, швидше за все, він хоче щось для паралельних сценаріїв, один сценарій встановлює глобальні, а інший читає їх. Інакше немає сенсу залучати setx, набір буде достатнім.
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.

це також для% CLIENTNAME%? - не працювали для мене - stackoverflow.com/questions/37550160/…
Ігор Л.

% CLIENTNAME% недоступний у моєму середовищі, і, прочитавши ваше запитання, я вважаю, що це щось встановлене зовнішнім процесом. (Коли процес запускає дочірній процес, він може налаштувати середовище для цієї дитини.) Оскільки він не є частиною фактичних змінних оточення, цей сценарій не буде оновлений.
DieterDP

Привіт @DieterDP, твоє рішення працює для мене! Я використовую Windows 10 на 64-розрядній машині. Я отримую помилку: "ПОМИЛКА. Система не змогла знайти вказаний ключ реєстру або значення." Тим не менш, оновлення змінних середовища пройшло успішно. Звідки походить помилка?
К.Мульєр

Важко сказати, без власне його тестування, але я здогадуюсь, структура реєстру на W10 може дещо відрізнятися. Якщо вам це подобається, спробуйте усунути помилку, виконавши команди в командному рядку.
DieterDP

2

Якщо це стосується лише одного (або декількох) конкретних варіантів, які ви хочете змінити, я вважаю, що найпростішим способом є вирішення : просто встановіть у своєму оточенні та в поточному сеансі консолі

  • Set встановить var у вашому поточному сеансі
  • SetX поставить var у оточення, але НЕ у вашому поточному сеансі

У мене є такий простий пакетний сценарій, щоб змінити мій 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 вам довелося вийти з системи та увійти назад, щоб врахувати зміни ...


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 році (Дійсно, оновити оболонку cmd непросто, оскільки це єдиний екземпляр, як згадувалося вище), оскільки оновлення змінних оточуючих середовищ у Windows дозволяє виконати безліч завдань з автоматизації без потрібно вручну перезапустити командний рядок.

Наприклад, ми використовуємо це, щоб дозволити розгортання та налаштування програмного забезпечення на великій кількості машин, які ми регулярно встановлюємо. І я мушу визнати, що перезапуск командного рядка під час розгортання нашого програмного забезпечення було б дуже недоцільним і вимагало від нас пошуку обхідних завдань, які не обов'язково приємні. Давайте перейдемо до нашої проблеми. Ми поступаємо так.

1 - У нас є пакетний сценарій, який, в свою чергу, викликає такий сценарій, як цей

[файл: task.cmd] .

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

2 - Після цього сценарій refresh.ps1 поновлює змінні середовища за допомогою ключів реєстру (GetValueNames () тощо). Тоді, в тому ж самому скрипті повноважень, нам просто потрібно викликати доступні нові змінні середовища. Наприклад, у типовому випадку, якщо ми тільки що раніше встановили 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

Як тільки скрипт повноважень закінчиться, сценарій cmd продовжується з іншими завданнями. Тепер слід пам’ятати, що після завершення завдання cmd все ще не має доступу до нових змінних середовища, навіть якщо скрипт повноважень оновив їх на своєму сеансі. Ось чому ми виконуємо всі необхідні завдання в скрипті powerhell, який може викликати ті ж команди, що і cmd.


0

Редагувати: це працює лише в тому випадку, якщо зміни середовища, які ви робите, є результатом запуску пакетного файлу.

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

Майже кожен пакетний файл, який я запису, починається з SETLOCALтого, що в більшості випадків я не хочу, щоб побічні ефекти змін середовища залишалися. У випадках, коли я хочу, щоб певні зміни змінної середовища розповсюджувалися поза пакетним файлом, то останній мій ENDLOCALвиглядає так:

ENDLOCAL & (
  SET RESULT1=%RESULT1%
  SET RESULT2=%RESULT2%
)

-1

Щоб вирішити це, я змінив змінну середовища за допомогою BOTH setx і set, а потім перезапустив усі екземпляри Explor.exe. Таким чином, будь-який процес, розпочатий згодом, матиме нову змінну середовища.

Мій пакетний сценарій для цього:

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

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

Проблема такого підходу полягає в тому, що всі вікна дослідників, які зараз відкриваються, будуть закриті, що, мабуть, погана ідея. Але дивіться пост Кева, щоб дізнатися, чому це потрібно

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.