Якщо я змінюю або додаю змінну середовища, мені доведеться перезапустити командний рядок. Чи є команда, яку я міг би виконати, що зробить це без перезавантаження CMD?
Якщо я змінюю або додаю змінну середовища, мені доведеться перезапустити командний рядок. Чи є команда, яку я міг би виконати, що зробить це без перезавантаження CMD?
Відповіді:
Ви можете захоплювати змінні системного середовища за допомогою сценарію 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"
Ось що використовує 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 .
RefreshEnv
щоб отримати оновлені змінні середовища у свій поточний сеанс.
Powershell
теж має працювати ? Це, здається, працює cmd.exe
для мене.
У Windows 7/8/10 ви можете встановити Chocolatey, у якого є сценарій для цього вбудованого.
Після встановлення Chocolatey просто введіть refreshenv
.
За задумом немає вбудованого механізму для Windows для поширення змінної середовища додавання / зміна / видалення до вже запущеного cmd.exe, або з іншого cmd.exe, або з "Мій комп'ютер -> Властивості -> Розширені налаштування -> Змінні середовища ".
Якщо ви змінюєте або додаєте нову змінну середовища за межами області існуючої відкритої командної лінії, вам потрібно буде або перезапустити командний рядок, або вручну додати за допомогою SET у існуючий командний рядок.
В останні загальноприйнятий відповідь показує часткове обхідним шляхом вручну оновити всі змінні оточення в скрипті. Сценарій обробляє випадок використання змінних змінних середовищ у всьому світі в "Мій комп'ютер ... Змінні середовища", але якщо змінна середовища змінена в одному cmd.exe, скрипт не поширюватиме його на інший запущений cmd.exe.
Я натрапив на цю відповідь, перш ніж врешті-решт знайти більш просте рішення.
Просто перезапустіть explorer.exe
у Диспетчері завдань.
Я не тестував, але вам може знадобитися знову відкрити командний рядок.
Кредит Тимо Хуовінену тут: Вузол не розпізнаний, хоча він успішно встановлений (якщо це допомогло вам, будь ласка, відправте коментар цього чоловіка)
cmd
вікно як адміністратор. Використовуйте команду taskkill /f /im explorer.exe && explorer.exe
. Це вб'є процес explor.exe і перезапустить його.
Це працює на Windows 7: SET PATH=%PATH%;C:\CmdShortcuts
перевірено, набравши echo% PATH%, і воно спрацювало, добре. також встановіть, якщо ви відкриєте новий cmd, більше не потрібно в цих примхливих перезавантаженнях :)
setx
оскільки він успадковує поточне середовище, яке може мати змінені зміни, а не те, що я хочу постійно. Це дозволяє мені уникнути перезавантаження консолі з метою використання змінних, уникаючи при цьому проблеми їх недоступності у всьому світі.
Використовуйте "setx" та перезапустіть cmd-рядок
Для цього завдання існує інструмент командного рядка з назвою " setx ". Це для читання та письма змінних env. Змінні зберігаються після закриття вікна команд.
Він "Створює або модифікує змінні середовища в користувальницькому або системному середовищі, не вимагаючи програмування або сценаріїв. Команда setx також витягує значення ключів реєстру і записує їх у текстові файли."
Примітка: змінні, створені або модифіковані цим інструментом, будуть доступні у майбутніх вікнах команд, але не у поточному вікні команд CMD.exe. Отже, вам доведеться перезапустити.
Якщо setx
відсутній:
Або змінити реєстр
MSDN каже:
Щоб програмно додати або змінити змінні системного середовища, додайте їх до ключа реєстру HKEY_LOCAL_MACHINE \ System \ CurrentControlSet \ Control \ Session Manager \ Environment , а потім транслюйте WM_SETTINGCHANGE повідомлення з lParam, встановленим у рядок " Environment ".
Це дозволяє програмам, таким як оболонка, збирати ваші оновлення.
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment\VARIABLE
поточне середовище користувача: HKEY_CURRENT_USER\Environment\VARIABLE
%PATH%
то setx
можете скоротити це до 1024 байтів! І просто так, його вечір зник
Виклик цієї функції працював для мене:
VOID Win32ForceSettingsChange()
{
DWORD dwReturnValue;
::SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, (LPARAM) "Environment", SMTO_ABORTIFHUNG, 5000, &dwReturnValue);
}
Найкращий метод, який я придумав, - це просто зробити запит у реєстрі. Ось мій приклад.
У моєму прикладі я зробив установку за допомогою файлу 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
Найпростіший спосіб додати змінну до шляху без перезавантаження для поточного сеансу - це відкрити командний рядок і ввести:
PATH=(VARIABLE);%path%
і натисніть enter.
щоб перевірити, чи завантажена ваша змінна, введіть
PATH
і натисніть enter. Однак змінна буде лише частиною шляху до перезавантаження.
Це можна зробити, перезаписавши таблицю навколишнього середовища в межах визначеного процесу.
Як доказ концепції я написав цей зразок програми, який щойно відредагував одну (відому) змінну середовища в процесі 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).
Змінні середовища зберігаються у HKEY_LOCAL_MACHINE \ SYSTEM \ ControlSet \ Control \ Session Manager \ Environment.
Багато корисних середовищ, таких як Path, зберігаються як REG_SZ. Існує кілька способів доступу до реєстру, включаючи REGEDIT:
REGEDIT /E <filename> "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
Неприємним може бути те, що є кілька місць, з яких можна починати cmd. У моєму випадку я запустив cmd від windows Explorer, і змінні середовища не змінилися, коли при запуску cmd з "run" (клавіша Windows + r) змінені середовища були змінені .
У моєму випадку мені просто довелося вбити процес Windows Explorer з диспетчера завдань, а потім знову запустити його з менеджера завдань .
Як тільки я це зробив, я отримав доступ до нової змінної середовища з cmd, що був породжений Windows Explorer.
Я використовую такий код у своїх пакетних сценаріях:
if not defined MY_ENV_VAR (
setx MY_ENV_VAR "VALUE" > nul
set MY_ENV_VAR=VALUE
)
echo %MY_ENV_VAR%
Використовуючи SET після SETX , можна використовувати змінну "local" безпосередньо без перезавантаження вікна команд. І при наступному запуску буде використана змінна середовище.
Мені сподобався підхід, за яким слідував шоколадний, як розміщено у відповіді анонімного боягуза, оскільки це чистий пакетний підхід. Однак він залишає тимчасовий файл і деякі тимчасові змінні, що лежать навколо. Я зробив для себе більш чисту версію.
Зробіть файл 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.
Якщо це стосується лише одного (або декількох) конкретних варіантів, які ви хочете змінити, я вважаю, що найпростішим способом є вирішення : просто встановіть у своєму оточенні та в поточному сеансі консолі
У мене є такий простий пакетний сценарій, щоб змінити мій 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 ...
Рішення, яке я використовую вже кілька років:
@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, ось оновлена версія.
Немає прямого шляху, як сказав Кев. У більшості випадків простіше нерестувати інший ящик CMD. Більш прикро, що запущені програми також не знають про зміни (хоча у IIRC може бути широкомовне повідомлення, яке слідкувати за тим, щоб отримувати повідомлення про такі зміни).
Це було гірше: у старих версіях Windows вам довелося вийти з системи та увійти назад, щоб врахувати зміни ...
Я використовую цей скрипт 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%"
Дякую за те, що ви написали це питання, що є досить цікавим, навіть у 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.
Редагувати: це працює лише в тому випадку, якщо зміни середовища, які ви робите, є результатом запуску пакетного файлу.
Якщо пакетний файл починається з SETLOCAL
цього пункту, він завжди вийде до вихідного середовища при виході, навіть якщо ви забудете зателефонуватиENDLOCAL
до виходу пакету або якщо він несподівано перерветься.
Майже кожен пакетний файл, який я запису, починається з SETLOCAL
того, що в більшості випадків я не хочу, щоб побічні ефекти змін середовища залишалися. У випадках, коли я хочу, щоб певні зміни змінної середовища розповсюджувалися поза пакетним файлом, то останній мій ENDLOCAL
виглядає так:
ENDLOCAL & (
SET RESULT1=%RESULT1%
SET RESULT2=%RESULT2%
)
Щоб вирішити це, я змінив змінну середовища за допомогою BOTH setx і set, а потім перезапустив усі екземпляри Explor.exe. Таким чином, будь-який процес, розпочатий згодом, матиме нову змінну середовища.
Мій пакетний сценарій для цього:
setx /M ENVVAR "NEWVALUE"
set ENVVAR="NEWVALUE"
taskkill /f /IM explorer.exe
start explorer.exe >nul
exit
Проблема такого підходу полягає в тому, що всі вікна дослідників, які зараз відкриваються, будуть закриті, що, мабуть, погана ідея. Але дивіться пост Кева, щоб дізнатися, чому це потрібно