Есть ли команда для обновления переменных среды из командной строки в Windows?
Если я изменяю или добавляю переменную среды, мне нужно перезапустить командную строку. Могу ли я выполнить команду, которая сделает это без перезапуска CMD?
37 ответов
Вы можете перехватить системные переменные среды с помощью сценария 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"
В Windows 7/8/10 вы можете установить Chocolatey, в котором есть встроенный скрипт.
После установки Chocolatey просто наберите "refreshenv" без кавычек.
Вот что использует 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 .
В Windows нет встроенного механизма распространения переменной среды add/change/remove в уже запущенный cmd.exe, либо из другого cmd.exe, либо из "Мой компьютер -> Свойства -> Расширенные настройки -> Переменные среды ".
Если вы изменяете или добавляете новую переменную среды вне области существующей открытой командной строки, вам необходимо либо перезапустить командную строку, либо добавить вручную, используя SET в существующей командной строке.
Последний принятый ответ показывает частичное обходное решение, вручную обновляя все переменные среды в скрипте. Сценарий обрабатывает сценарий изменения глобальных переменных среды в "Мой компьютер... Переменные среды", но если переменная среды изменяется в одном cmd.exe, сценарий не будет распространять его на другой запущенный cmd.exe.
Я наткнулся на этот ответ, прежде чем в конце концов нашел более простое решение.
Просто перезагрузите explorer.exe
в диспетчере задач.
Я не тестировал, но вам также может потребоваться снова открыть командную строку.
Благодарность Timo Huovinen здесь: Узел не распознан, хотя и успешно установлен (если это помогло вам, пожалуйста, дайте оценку этому человеку за комментарии).
Это работает на Windows 7: SET PATH=%PATH%;C:\CmdShortcuts
проверил набрав echo %PATH% и все заработало, отлично. также установите, если вы открываете новый cmd, больше не нужны эти надоедливые перезагрузки:)
Используйте "setx" и перезапустите командную строку
Для этой работы есть инструмент командной строки с именем " setx ". Это для чтения и записи переменных env. Переменные сохраняются после закрытия командного окна.
Он "Создает или изменяет переменные среды в пользовательской или системной среде, не требуя программирования или создания сценариев. Команда setx также извлекает значения ключей реестра и записывает их в текстовые файлы".
Примечание: переменные, созданные или измененные этим инструментом, будут доступны в будущих окнах команд, но не в текущем окне команд CMD.exe. Итак, вы должны перезагрузить.
Если setx
пропал, отсутствует:
Или измените реестр
MSDN говорит:
Чтобы программно добавить или изменить системные переменные среды, добавьте их в раздел реестра HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment, а затем отправьте сообщение WM_SETTINGCHANGE с параметром lParam для строки " Environment ".
Это позволяет приложениям, таким как оболочка, забирать ваши обновления.
Вызов этой функции сработал для меня:
VOID Win32ForceSettingsChange()
{
DWORD dwReturnValue;
::SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, (LPARAM) "Environment", SMTO_ABORTIFHUNG, 5000, &dwReturnValue);
}
Я сделал лучшую альтернативу Chocolatey refreshenv для cmd и cygwin, которая решает множество проблем, таких как:
Chocolatey refreshenv настолько плох, если переменная имеет некоторые метасимволы cmd, см. Этот тест:
добавьте это в путь в HKCU\Environment:
test & echo baaaaaaaaaad
, и запустите шоколадныйrefreshenv
вы увидите, что он печатаетbaaaaaaaaaad
что очень плохо, и новый путь не добавляется к вашей переменной пути.Этот скрипт решает эту проблему, и вы можете протестировать его с любым метасимволом, даже с таким плохим, как:
; & % ' ( ) ~ + @ # $ { } [ ] , ` ! ^ | > < \ / " : ? * = . - _ & echo baaaad
refreshenv добавляет только системные и пользовательские переменные среды, но CMD также добавляет изменчивые переменные (HKCU\Volatile Environment). Этот сценарий объединит все три и удалит все дубликаты .
refreshenv сбросит ваш PATH. Этот сценарий добавляет новый путь к старому пути родительского сценария, который вызывал этот сценарий. Это лучше, чем перезапись старого пути, иначе он удалит любой новый путь, добавленный родительским скриптом.
Этот сценарий решает проблему, описанную в комментарии @GeneMayevsky: refreshenv изменяет переменные окружения TEMP и TMP, заменяя их значениями, хранящимися в HKCU\Environment. В моем случае я запускаю сценарий для обновления переменных env, измененных заданием Jenkins, на ведомом устройстве, работающем под учетной записью SYSTEM, поэтому TEMP и TMP заменяются%USERPROFILE%\AppData\Local\Temp вместо C:\Windows\Temp. Это прерывает сборку, поскольку компоновщик не может открыть папку Temp системного профиля.
Я сделал один скрипт для cmd, а другой - для cygwin / bash, вы можете найти его в github здесь: https://github.com/badrelmers/RefrEnv
для cmd
этот скрипт использует vbscript, поэтому он работает во всех версиях Windows xp+
чтобы использовать его, сохраните его как refrenv.bat и вызовите с помощью
call refrenv.bat
<!-- : Begin batch script
@echo off
REM PUSHD "%~dp0"
REM author: Badr Elmers 2021
REM description: refrenv = refresh environment. this is a better alternative to the chocolatey refreshenv for cmd
REM ___USAGE_____________________________________________________________
REM usage: from your script call this script as: call refrenv.bat
REM you can also put this script in windows\systems32 or another place in your %PATH% then call it from an interactive console by writing refrenv
REM ___DEBUG_____________________________________________________________
REM to debug what this script do uncomment this variable (debugme), then the folder containing the files used to set the variables will be open ;then see _NewEnv.cmd this is the file which run inside your script to setup the new variables, you can also revise the intermediate files _NewEnv.cmd_temp_.cmd and _NewEnv.cmd_temp2_.cmd (those two contains all the variables before removing the duplicates and the unwanted variables)
REM set debugme=yes
REM ___HELP_____________________________________________________________
REM :: this script reload environment variables inside cmd every time you want environment changes to propagate, so you do not need to restart cmd after setting a new variable with setx or when installing new apps which add new variables ...etc
REM # for PATH: this script append the new paths to the old path of the parent script which called this script; its better than overrwiting the old path; otherwise it will delete any newly added path by the parent script
REM ________
REM this script solve things like that too:
REM The confusing thing might be that there are a few places to start the cmd from. In my case I run cmd from windows explorer and the environment variables did not change while when starting cmd from the "run" (windows key + r) the environment variables were changed.
REM In my case I just had to kill the windows explorer process from the task manager and then restart it again from the task manager.
REM Once I did this I had access to the new environment variable from a cmd that was spawned from windows explorer.
REM my conclusion:
REM if I add a new variable with setx, i can access it in cmd only if i run cmd as admin, without admin right i have to restart explorer to see that new variable. but running this script inside my script (who sets the variable with setx) solve this problem and i do not have to restart explorer
REM ________
REM windows recreate the path using three places at less:
REM the User namespace: HKCU\Environment
REM the System namespace: HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment
REM the Session namespace: HKCU\Volatile Environment
REM but the original chocolatey script did not add the volatil path, so lets add it because this is what windows do by default
REM there is this too which cmd seems to read when first running, but it contains only TEMP and TMP,so i will not use it
REM HKEY_USERS\.DEFAULT\Environment
REM ___TESTING_____________________________________________________________
REM to test this script with extreme cases do
REM :: Set a bad variable
REM add a var in reg HKCU\Environment as the following, and see that echo is not executed. if you use refreshenv of chocolatey you will see that echo is executed which is so bad!
REM so save this in reg:
REM all 32 charachters: & % ' ( ) ~ + @ # $ { } [ ] ; , ` ! ^ | > < \ / " : ? * = . - _ & echo baaaad
REM and this:
REM (^.*)(Form Product=")([^"]*") FormType="[^"]*" FormID="([0-9][0-9]*)".*$
REM and use set to print those variables and see if they are saved without change ; refreshenv fail dramatically with those variables
REM invalid characters (illegal characters in file names) in Windows using NTFS
REM \ / : * ? " < > | and ^ in FAT
REM __________________________________________________________________________________________
REM __________________________________________________________________________________________
REM __________________________________________________________________________________________
REM this is a hybrid script which call vbs from cmd directly
REM :: The only restriction is the batch code cannot contain - - > (without space between - - > of course)
REM :: The only restriction is the VBS code cannot contain </script>.
REM :: The only risk is the undocumented use of "%~f0?.wsf" as the script to load. Somehow the parser properly finds and loads the running .BAT script "%~f0", and the ?.wsf suffix mysteriously instructs CSCRIPT to interpret the script as WSF. Hopefully MicroSoft will never disable that "feature".
REM :: https://stackoverflow.com/questions/9074476/is-it-possible-to-embed-and-execute-vbscript-within-a-batch-file-without-using-a
set "TEMPDir=%TEMP%\refrenv"
IF NOT EXIST "%TEMPDir%" mkdir "%TEMPDir%"
set "outputfile=%TEMPDir%\_NewEnv.cmd"
REM detect if DelayedExpansion is enabled
REM It relies on the fact, that the last caret will be removed only in delayed mode.
REM https://www.dostips.com/forum/viewtopic.php?t=6496
set "DelayedExpansionState=IsDisabled"
IF "^!" == "^!^" (
REM echo DelayedExpansion is enabled
set "DelayedExpansionState=IsEnabled"
)
REM :: generate %outputfile% which contain all the new variables
REM cscript //nologo "%~f0?.wsf" %1
cscript //nologo "%~f0?.wsf" "%outputfile%" %DelayedExpansionState%
REM ::set the new variables generated with vbscript script above
REM for this to work always it is necesary to use DisableDelayedExpansion or escape ! and ^ when using EnableDelayedExpansion, but this script already solve this, so no worry about that now, thanks to God
REM test it with some bad var like:
REM all 32 charachters: ; & % ' ( ) ~ + @ # $ { } [ ] , ` ! ^ | > < \ / " : ? * = . - _ & echo baaaad
REM For /f delims^=^ eol^= %%a in (%outputfile%) do %%a
REM for /f "delims== tokens=1,2" %%G in (%outputfile%) do set "%%G=%%H"
For /f delims^=^ eol^= %%a in (%outputfile%) do set %%a
REM for safely print a variable with bad charachters do:
REM SETLOCAL EnableDelayedExpansion
REM echo "!z9!"
REM or
REM set z9
REM but generally paths and environment variables should not have bad metacharacters, but it is not a rule!
if "%debugme%"=="yes" (
explorer "%TEMPDir%"
) else (
rmdir /Q /S "%TEMPDir%"
)
REM cleanup
set "TEMPDir="
set "outputfile="
set "DelayedExpansionState="
set "debugme="
REM pause
exit /b
REM #############################################################################
REM :: to run jscript you have to put <script language="JScript"> directly after ----- Begin wsf script --->
----- Begin wsf script --->
<job><script language="VBScript">
REM #############################################################################
REM ### put you code here #######################################################
REM #############################################################################
REM based on itsadok script from here
REM https://stackoverflow.com/questions/171588/is-there-a-command-to-refresh-environment-variables-from-the-command-prompt-in-w
REM and it is faster as stated by this comment
REM While I prefer the Chocolatey code-wise for being pure batch code, overall I decided to use this one, since it's faster. (~0.3 seconds instead of ~1 second -- which is nice, since I use it frequently in my Explorer "start cmd here" entry) –
REM and it is safer based on my tests, the Chocolatey refreshenv is so bad if the variable have some cmd metacharacters
Const ForReading = 1
Const ForWriting = 2
Const ForAppending = 8
Set WshShell = WScript.CreateObject("WScript.Shell")
REM filename = WshShell.ExpandEnvironmentStrings("%TEMP%\resetvars.bat")
REM filename = "resetvars.bat"
filename=WScript.Arguments.Item(0)
DelayedExpansionState=WScript.Arguments.Item(1)
TMPfilename=filename & "_temp_.cmd"
Set fso = CreateObject("Scripting.fileSystemObject")
Set tmpF = fso.CreateTextFile(TMPfilename, TRUE)
set oEnvS=WshShell.Environment("System")
for each sitem in oEnvS
REM tmpF.WriteLine("SET " & sitem)
tmpF.WriteLine(sitem)
next
SystemPath = oEnvS("PATH")
set oEnvU=WshShell.Environment("User")
for each sitem in oEnvU
REM tmpF.WriteLine("SET " & sitem)
tmpF.WriteLine(sitem)
next
UserPath = oEnvU("PATH")
set oEnvV=WshShell.Environment("Volatile")
for each sitem in oEnvV
REM tmpF.WriteLine("SET " & sitem)
tmpF.WriteLine(sitem)
next
VolatilePath = oEnvV("PATH")
set oEnvP=WshShell.Environment("Process")
REM i will not save the process env but only its path, because it have strange variables like =::=::\ and =F:=.... which seems to be added by vbscript
REM for each sitem in oEnvP
REM tmpF.WriteLine("SET " & sitem)
REM next
REM aki anado el path actual asi no se resetea el path ya ke kizas haya anadido algo al path con el script ke llamara este script, si kiero resetear el path entonces comment this
ProcessPath = oEnvP("PATH")
NewPath = SystemPath & ";" & UserPath & ";" & VolatilePath & ";" & ProcessPath
REM NewPath = SystemPath & ";" & UserPath & ";" & VolatilePath
REM NewPath = SystemPath & ";" & UserPath
REM ________________________________________________________________
REM :: remove duplicates from path
REM :: expand variables so they become like windows do when he read reg and create path, then Remove duplicates without sorting
REM why i will clean the path from duplicates? because:
REM the maximum string length in cmd is 8191 characters. But string length doesnt mean that you can save 8191 characters in a variable because also the assignment belongs to the string. you can save 8189 characters because the remaining 2 characters are needed for "a="
REM based on my tests:
REM when i open cmd as user , windows does not remove any duplicates from the path, and merge system+user+volatil path
REM when i open cmd as admin, windows do: system+user path (here windows do not remove duplicates which is stupid!) , then it adds volatil path after removing from it any duplicates
REM ' https://www.rosettacode.org/wiki/Remove_duplicate_elements#VBScript
Function remove_duplicates(list)
arr = Split(list,";")
Set dict = CreateObject("Scripting.Dictionary")
REM ' force dictionary comapre to be case-insensitive , uncomment to force case-sensitive
dict.CompareMode = 1
For i = 0 To UBound(arr)
If dict.Exists(arr(i)) = False Then
dict.Add arr(i),""
End If
Next
For Each key In dict.Keys
tmp = tmp & key & ";"
Next
remove_duplicates = Left(tmp,Len(tmp)-1)
End Function
REM WScript.Echo remove_duplicates("b;B;g;a;a;b;b;c;d;e;d;f;f;f;g;h")
REM expand variables
NewPath = WshShell.ExpandEnvironmentStrings(NewPath)
REM remove duplicates
NewPath=remove_duplicates(NewPath)
tmpF.WriteLine("PATH=" & NewPath)
tmpF.Close
REM ________________________________________________________________
REM :: exclude setting variables which may be dangerous to change
REM when i run a script from task scheduler using SYSTEM user the following variables are the differences between the scheduler env and a normal cmd script, so i will not override those variables
REM APPDATA=D:\Users\LLED2\AppData\Roaming
REM APPDATA=D:\Windows\system32\config\systemprofile\AppData\Roaming
REM LOCALAPPDATA=D:\Users\LLED2\AppData\Local
REM LOCALAPPDATA=D:\Windows\system32\config\systemprofile\AppData\Local
REM TEMP=D:\Users\LLED2\AppData\Local\Temp
REM TEMP=D:\Windows\TEMP
REM TMP=D:\Users\LLED2\AppData\Local\Temp
REM TMP=D:\Windows\TEMP
REM USERDOMAIN=LLED2-PC
REM USERDOMAIN=WORKGROUP
REM USERNAME=LLED2
REM USERNAME=LLED2-PC$
REM USERPROFILE=D:\Users\LLED2
REM USERPROFILE=D:\Windows\system32\config\systemprofile
REM i know this thanks to this comment
REM The solution is good but it modifies env variables TEMP and TMP replacing them with values stored in HKCU\Environment. In my case I run the script to update env variables modified by Jenkins job on a slave that's running under SYSTEM account, so TEMP and TMP get substituted by %USERPROFILE%\AppData\Local\Temp instead of C:\Windows\Temp. This breaks build because linker cannot open system profile's Temp folder. – Gene Mayevsky Sep 26 '19 at 20:51
REM ' Delete Lines of a Text File Beginning with a Specified String
REM those are the variables which sould not be changed by this script
arrBlackList = Array("ALLUSERSPROFILE=", "APPDATA=", "CommonProgramFiles=", "CommonProgramFiles(x86)=", "CommonProgramW6432=", "COMPUTERNAME=", "ComSpec=", "HOMEDRIVE=", "HOMEPATH=", "LOCALAPPDATA=", "LOGONSERVER=", "NUMBER_OF_PROCESSORS=", "OS=", "PATHEXT=", "PROCESSOR_ARCHITECTURE=", "PROCESSOR_ARCHITEW6432=", "PROCESSOR_IDENTIFIER=", "PROCESSOR_LEVEL=", "PROCESSOR_REVISION=", "ProgramData=", "ProgramFiles=", "ProgramFiles(x86)=", "ProgramW6432=", "PUBLIC=", "SystemDrive=", "SystemRoot=", "TEMP=", "TMP=", "USERDOMAIN=", "USERDOMAIN_ROAMINGPROFILE=", "USERNAME=", "USERPROFILE=", "windir=", "SESSIONNAME=")
Set objFS = CreateObject("Scripting.FileSystemObject")
Set objTS = objFS.OpenTextFile(TMPfilename, ForReading)
strContents = objTS.ReadAll
objTS.Close
TMPfilename2= filename & "_temp2_.cmd"
arrLines = Split(strContents, vbNewLine)
Set objTS = objFS.OpenTextFile(TMPfilename2, ForWriting, True)
REM this is the equivalent of findstr /V /I /L or grep -i -v , i don t know a better way to do it, but it works fine
For Each strLine In arrLines
bypassThisLine=False
For Each BlackWord In arrBlackList
If Left(UCase(LTrim(strLine)),Len(BlackWord)) = UCase(BlackWord) Then
bypassThisLine=True
End If
Next
If bypassThisLine=False Then
REM wscript.echo "___" & strLine & "+++" & BlackWord
objTS.WriteLine strLine
End If
Next
REM ____________________________________________________________
REM :: expand variables because registry save some variables as unexpanded %....%
REM :: escape ! and ^ for cmd EnableDelayedExpansion mode
set f=fso.OpenTextFile(TMPfilename2,ForReading)
REM Write file: ForAppending = 8 ForReading = 1 ForWriting = 2 , True=create file if not exist
set fW=fso.OpenTextFile(filename,ForWriting,True)
Do Until f.AtEndOfStream
LineContent = f.ReadLine
REM expand variables
LineContent = WshShell.ExpandEnvironmentStrings(LineContent)
REM _____this part is so important_____
REM if cmd delayedexpansion is enabled in the parent script which calls this script then bad thing happen to variables saved in the registry if they contain ! . if var have ! then ! and ^ are removed; if var do not have ! then ^ is not removed . to understand what happens read this :
REM how cmd delayed expansion parse things
REM https://stackoverflow.com/questions/4094699/how-does-the-windows-command-interpreter-cmd-exe-parse-scripts/7970912#7970912
REM For each parsed token, first check if it contains any !. If not, then the token is not parsed - important for ^ characters. If the token does contain !, then scan each character from left to right:
REM - If it is a caret (^) the next character has no special meaning, the caret itself is removed
REM - If it is an exclamation mark, search for the next exclamation mark (carets are not observed anymore), expand to the value of the variable.
REM - Consecutive opening ! are collapsed into a single !
REM - Any remaining unpaired ! is removed
REM ...
REM Look at next string of characters, breaking before !, :, or <LF>, and call them VAR
REM conclusion:
REM when delayedexpansion is enabled and var have ! then i have to escape ^ and ! ,BUT IF VAR DO NOT HAVE ! THEN DO NOT ESCAPE ^ .this made me crazy to discover
REM when delayedexpansion is disabled then i do not have to escape anything
If DelayedExpansionState="IsEnabled" Then
If InStr(LineContent, "!") > 0 Then
LineContent=Replace(LineContent,"^","^^")
LineContent=Replace(LineContent,"!","^!")
End If
End If
REM __________
fW.WriteLine(LineContent)
Loop
f.Close
fW.Close
REM #############################################################################
REM ### end of vbscript code ####################################################
REM #############################################################################
REM this must be at the end for the hybrid trick, do not remove it
</script></job>
для cygwin / bash:
сохраните его как refrenv.sh и вызовите из bash с помощью:
source refrenv.sh
# usage: source refrenv.sh
getNewlyAddedVars(){
# get the newly added variables from registry which are not defined in right now in bash
regPath="$1"
echo "look for new vars in $regPath"
IFSorg=$IFS
IFS=$'\n'
# print all reg variables and remove critical ones
# --list es importante sino se imprime tb los subfolders dentro del path, volatile por ejemplo tiene un folder/subkey llamada 1 y se imprime tb si no uso --list
local regvar=""
for i in $(regtool list --list "${regPath}" | grep -i -v -e '^ALLUSERSPROFILE$' -e '^APPDATA$' -e '^CommonProgramFiles$' -e '^CommonProgramFiles(x86)$' -e '^CommonProgramW6432$' -e '^COMPUTERNAME$' -e '^ComSpec$' -e '^HOMEDRIVE$' -e '^HOMEPATH$' -e '^LOCALAPPDATA$' -e '^LOGONSERVER$' -e '^NUMBER_OF_PROCESSORS$' -e '^OS$' -e '^PATHEXT$' -e '^PROCESSOR_ARCHITECTURE$' -e '^PROCESSOR_ARCHITEW6432$' -e '^PROCESSOR_IDENTIFIER$' -e '^PROCESSOR_LEVEL$' -e '^PROCESSOR_REVISION$' -e '^ProgramData$' -e '^ProgramFiles$' -e '^ProgramFiles(x86)$' -e '^ProgramW6432$' -e '^PUBLIC$' -e '^SystemDrive$' -e '^SystemRoot$' -e '^TEMP$' -e '^TMP$' -e '^USERDOMAIN$' -e '^USERDOMAIN_ROAMINGPROFILE$' -e '^USERNAME$' -e '^USERPROFILE$' -e '^windir$' -e '^SESSIONNAME$' -e '^LINES$' -e '^COLUMNS$' -e '^OLDPWD$') ; do
local regvar+="$i"$'\n'
done
# print all defined variables in bash
local bashvar=""
# met1:
# for i in $(set -o posix; set | grep '=') ; do
# bashvar+=$(echo "$i" | cut -d'=' -f1)$'\n'
# done
# met2
# The bash builtin compgen was meant to be used in completion scripts. To this end, compgen -v lists all defined variables. The downside: it lists only the variable names, not the values.
# es exactamente lo ke kiero ademas compgen -v imprime mas cosas ke set
local bashvar=$(compgen -v)
# compare reg variables to bash variables and print only variables that exist in reg and do not exist in bash, this of course means that if a variable is already defined in bash and it is updates in reg,then this variable will not be updated using this script, this is not good of course but it is safer, because i may override a critical bash variable otherwide
# so with this method i will get all the new added vairables but not the old variables which were recently updated
# show lines that exist in string2 and do not exist in string1
# grep's -x (--line-regexp) can be used to ensure the entire line is matched. So if A1 contains x and A2 contains xx, a match will not be found.
# You probably also need to use the option -F or --fixed-strings. Otherwise grep will be interpreting A1 as regular expressions. So if A1 contains the line .*, it will match everything. So the entire command would be: grep -vxF -f A1 A2
# he usado grep -i sino se considera Path a new var aunque ya esta difinida en bash, pk esta in uppercase PATH not Path as in reg
local newVars=$(grep -i -vxF -f <(echo "$bashvar") <(echo "$regvar"))
# extract the values of the final key
local ALLnewVarsKeysAndValues=""
# dont quote $newVars i already overided IFS so no worry about space, if i quote it the multiline is considered one word
for i in $newVars ; do
if [[ "$i" != "" ]] ; then
# si la variable tiene ' puede pasar cosas malas asi lets escape it with '\''
local newVarKeysAndValue0=$(regtool get "${regPath}/${i}" | sed "s/'/'\\\\''/g" )
local newVarKeysAndValue="export ${i}='${newVarKeysAndValue0}'"
local ALLnewVarsKeysAndValues+="${newVarKeysAndValue}"$'\n'
fi
done
printf '%s' "$ALLnewVarsKeysAndValues" >> ${TEMP}/newEnv.sh
IFS="$IFSorg"
}
getNewPATHS(){
local HKLM=$(regtool get '/HKLM/System/CurrentControlSet/Control/Session Manager/Environment/path')
local HKCU=$(regtool get '/HKCU/Environment/path')
local HKCUV=$(regtool get '/HKCU/Volatile Environment/path')
local allPATHs="${HKLM};${HKCU};${HKCUV}"
# al llamar 'cmd /c echo...' abajo y si la variable tiene " ,bash anadira un slash antes " y hara ke la comparacion mas abajo no detecte ke hay un doble pk ahora sera \" en vez de " , asi ke vamos a borrar todas la letras ke no pueden existir in a dir path
# REM porke a veces por error puede ke el path tenga charachteres malos como :
# REM invalid characters (illegal characters) in Windows using NTFS
# REM \ / : * ? " < > | and ^ in FAT
# REM ninguno de estos me da problemas con bash
# REM pero para estar seguro y puesto ke estos charachteres no pueden existir in folders paths then let s just remove them except / \ and :
local allPATHsClean=$(printf '%s' "$allPATHs" | tr -d '*?"<>|')
# let s expand the path variables
# expandir con cmd es peligroso , si la variable tiene % me dara problemas, y no puedo borrar o escape % pk batch usa % para la variables y ahora kiero expandir la variables, asi ke cmd no me sirve, asi ke vamos a hacerlo con vbs
# local allPATHsClean=$(printf '%s' "$allPATHsClean" | sed 's/%/%%/g')
# local AllExpandedPaths=$(cmd /c "echo ${allPATHsClean}")
# cmd /c echo imprime \r (CR) asi ke lo borrammos
# local AllExpandedPaths=$(printf '%s' "$AllExpandedPaths" | tr -d '\r')
printf '%s' "$allPATHsClean" > ${TEMP}/sssource.txt
printf '%s' '
strFilename=WScript.Arguments.Item(0)
outFilename=WScript.Arguments.Item(1)
Set fso=CreateObject("Scripting.FileSystemObject")
REM Read file
set objFile=fso.OpenTextFile(strFilename,1)
oldContent=objFile.ReadAll
REM expand variables
set WshShell = WScript.CreateObject("WScript.Shell")
newContent = WshShell.ExpandEnvironmentStrings(oldContent)
REM Write file: ForAppending = 8 ForReading = 1 ForWriting = 2 , True=create file if not exist
set objFile=fso.OpenTextFile(outFilename,2,True)
objFile.Write newContent
objFile.Close' > ${TEMP}/rrrr.vbs
cscript //nologo $(cygpath -w ${TEMP}/rrrr.vbs) $(cygpath -w ${TEMP}/sssource.txt) $(cygpath -w ${TEMP}/outtt.txt)
AllExpandedPaths=$(cat ${TEMP}/outtt.txt)
rm ${TEMP}/sssource.txt ${TEMP}/outtt.txt ${TEMP}/rrrr.vbs
# I must append the new paths to the old path; its better than overrwiting the old path; otherwise i will delete any newly added path by the script who called this script
# first lest convert the windows path to the equivalent cygwin path format: cygdrive...
# clean PATH because if it contain " then bash will print it as \" y de todas formas " no debe existir in paths
local PATHclean=$(printf '%s' "$PATH" | tr -d '*?"<>|')
IFSorg=$IFS
IFS=':'
# local mypath=( $PATHclean )
# for i in ${mypath[@]} ; do
for i in ${PATHclean} ; do
local convertedPATHs+="$i"$'\n'
done
IFS=';'
# local AllExpandedPaths2=( $AllExpandedPaths )
# for i in ${AllExpandedPaths2[@]} ; do
for i in ${AllExpandedPaths} ; do
local convertedPATHs+=$(cygpath "$i")$'\n'
done
# remove the last slash / so i catch duplicates which differ in the last slash only .../:.../
local convertedPATHs=$(printf '%s' "$convertedPATHs" | sed 's/\/$//g')
# remove duplicates without sorting
# case sensitive
# local uniqpath=$(printf '%s' "$convertedPATHs" | nl | sort -u -k2 | sort -n | cut -f2-)
# case insensitive
local uniqpath=$(printf '%s' "$convertedPATHs" | nl | sort --ignore-case -u -k2 | sort -n | cut -f2-)
# convert it to cygwin PATH format ...:...:...ect
local finalpath=$(printf '%s' "$uniqpath" | tr '\n' ':')
# si la variable tiene ' puede pasar cosas malas asi lets escape it with '\''
local finalpath=$(printf '%s' "$finalpath" | sed "s/'/'\\\\''/g")
local finalpath="export PATH='${finalpath}'"
printf '%s' "$finalpath" >> ${TEMP}/newEnv.sh
IFS="$IFSorg"
}
main(){
rm ${TEMP}/newEnv.sh
getNewlyAddedVars '/HKLM/System/CurrentControlSet/Control/Session Manager/Environment'
getNewlyAddedVars '/HKCU/Environment'
getNewlyAddedVars '/HKCU/Volatile Environment'
getNewPATHS
# finally set the new variables
source ${TEMP}/newEnv.sh
# cat ${TEMP}/newEnv.sh
# env | sort
# del ${TEMP}/newEnv.sh
}
main
# read -p endddddd
Лучший способ, который я придумал, это просто сделать запрос в реестре. Вот мой пример.
В моем примере я сделал установку с использованием пакетного файла, в который были добавлены новые переменные среды. Мне нужно было что-то сделать, как только установка была завершена, но я не смог запустить новый процесс с этими новыми переменными. Я протестировал создание другого окна обозревателя и перезвонил cmd.exe, и это сработало, но в Vista и Windows 7 проводник работает только как один экземпляр и, как правило, как человек, вошедший в систему. Это не получится при автоматизации, так как мне нужны права администратора для делать вещи независимо от запуска из локальной системы или от имени администратора на поле. Ограничением является то, что он не обрабатывает такие вещи, как путь, это работает только для простых переменных окружения. Это позволило мне использовать пакет для перехода в каталог (с пробелами) и копирования в файлы, запускаемые.exes и т. Д. Это было написано сегодня из майских ресурсов на stackru.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
Перезапуск проводника сделал это для меня, но только для новых терминалов cmd.
Терминал, который я установил, мог уже видеть новую переменную Path (в Windows 7).
taskkill /f /im explorer.exe && explorer.exe
Это можно сделать, переписав Таблицу среды внутри самого указанного процесса.
В качестве подтверждения концепции я написал этот пример приложения, которое только что отредактировало одну (известную) переменную среды в процессе 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 происходит сбой).
Самый простой способ добавить переменную в путь без перезагрузки для текущего сеанса - это открыть командную строку и ввести:
PATH=(VARIABLE);%path%
и нажмите ввод.
чтобы проверить, загружена ли ваша переменная, введите
PATH
и нажмите ввод. Тем не менее, переменная будет только частью пути до перезагрузки.
Переменные среды хранятся в HKEY_LOCAL_MACHINE\SYSTEM\ControlSet\Control\Session Manager\Environment.
Многие из полезных переменных env, такие как Path, хранятся как REG_SZ. Есть несколько способов получить доступ к реестру, включая REGEDIT:
REGEDIT /E <filename> "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 "tokens=1,2* delims==" %%i in ('type %temp%\env.reg ^| findstr -c:\"Path\"=') do (установить upath=%%~j эхо!upath:\\=\! >% Temp% \ NewPath) ENDLOCAL for /f "tokens=*" %%i в (%temp%\newpath) установить путь =%%i
Попробуйте открыть новую командную строку от имени администратора. Это работало для меня на Windows 10. (Я знаю, что это старый ответ, но я должен был поделиться этим, потому что писать сценарий VBS просто для этого абсурдно).
Смущает то, что есть несколько мест для запуска cmd. В моем случае я запустил cmd из проводника Windows, и переменные среды не изменились, а при запуске cmd из "запуска" (windows key + r) переменные среды были изменены.
В моем случае мне просто нужно было убить процесс проводника Windows из диспетчера задач, а затем снова запустить его из диспетчера задач.
После этого я получил доступ к новой переменной среды из cmd, который был создан из проводника 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, вот обновленная версия.
Я использую следующий код в своих пакетных скриптах:
if not defined MY_ENV_VAR (
setx MY_ENV_VAR "VALUE" > nul
set MY_ENV_VAR=VALUE
)
echo %MY_ENV_VAR%
Используя SET после SETX, можно напрямую использовать локальную переменную без перезапуска командного окна. И при следующем запуске будет использоваться переменная окружения.
Если это относится только к одной (или нескольким) конкретным переменным, которые вы хотите изменить, я думаю, что самый простой способ - это обходной путь: просто установить в своей среде И в текущем сеансе консоли
- Set поместит переменную в ваш текущий сеанс
- SetX поместит переменную в среду, но НЕ в текущую сессию
У меня есть этот простой пакетный скрипт для изменения моего Maven с Java7 на Java8 (которые оба являются env. Vars). Пакетная папка находится в моей переменной PATH, поэтому я всегда могу вызвать 'j8' и в своей консоли, и в среде мой var JAVA_HOME меняется:
j8.bat:
@echo off
set JAVA_HOME=%JAVA_HOME_8%
setx JAVA_HOME "%JAVA_HOME_8%"
До сих пор я считаю, что это работает лучше и проще. Вы, вероятно, хотите, чтобы это было в одной команде, но ее просто нет в Windows...
Просто перезапустите explorer.exe >> протестировано на win 8 X64
Примерно с марта 2023 года Windows Terminal будет автоматически обновлять переменные среды на новых вкладках . Вам больше не нужно перезапускать весь терминал Windows, просто используйте новую вкладку.
Очевидно, что это не прямое решение вопроса, но это улучшение по сравнению с прошлым поведением.
Мне понравился подход, за которым следовал шоколад, как написано в анонимном ответе труса, так как это чисто пакетный подход. Тем не менее, он оставляет временный файл и некоторые временные переменные лежат вокруг. Я сделал более чистую версию для себя.
Сделать файл 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://stackru.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.
Сначала установите choco:
при использовании cmd
@"%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe" -NoProfile -InputFormat None -ExecutionPolicy Bypass -Command "iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))" && SET "PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin"
при использовании powershell
Set-ExecutionPolicy Bypass -Scope Process -Force; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
Тогда вы можете запустить refreshenv
, Работает как на cmd, так и на powershell.
Спасибо за публикацию этого вопроса, который весьма интересен, даже в 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.
Как сказал Кев, прямого пути нет. В большинстве случаев проще создать другую коробку CMD. Еще более досадно, что работающие программы также не знают об изменениях (хотя IIRC может показывать широковещательное сообщение, чтобы получать уведомления о таких изменениях).
Хуже было: в старых версиях Windows вам приходилось выходить из системы, а затем снова входить в систему, чтобы учесть изменения...
Вы можете использовать эту команду Nircmd , которая немедленно обновит среду:
nircmd sysrefresh environment
Если вам просто нужно обновить переменные среды в Windows, запустите refresh env из вашего терминала, чем перезагрузить компьютер.
Я использую этот скрипт 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%"
Нет, я так не думаю... вы можете установить их вручную, хотя. Таким образом, вы можете поместить их в пакетный файл или что-то.
вероятно, может создать утилиту / скрипт (если кто-то еще этого не сделал), который запрашивает реестр и устанавливает текущую среду такой же
for /f "usebackq tokens=* delims=" %# in (
`start /B /I powershell -c "(gci env:).ForEach({$_.Name+'='+$_.Value})"`) do @set "%#"
Если запущен в пакетном файле, используйте
%%#
вместо
%#
чтобы избежать знака процента.