如果修改或添加环境变量,则必须重新启动命令提示符。是否有一个命令,我可以执行,将这样做而不重新启动CMD?


当前回答

我做了一个更好的替代Chocolatey refreshenv cmd和cygwin,解决了很多问题,如:

The Chocolatey refreshenv is so bad if the variable have some cmd meta-characters, see this test: add this to the path in HKCU\Environment: test & echo baaaaaaaaaad, and run the chocolatey refreshenv you will see that it prints baaaaaaaaaad which is very bad, and the new path is not added to your path variable. This script solve this and you can test it with any meta-character, even something so bad like: ; & % ' ( ) ~ + @ # $ { } [ ] , ` ! ^ | > < \ / " : ? * = . - _ & echo baaaad refreshenv adds only system and user environment variables, but CMD adds volatile variables too (HKCU\Volatile Environment). This script will merge all the three and remove any duplicates. refreshenv reset your PATH. This script append the new path to the old path of the parent script which called this script. It is better than overwriting the old path, otherwise it will delete any newly added path by the parent script. This script solve this problem described in a comment here by @Gene Mayevsky: refreshenv 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.

我为cmd和cygwin/bash做了一个脚本, 你可以在这里找到他们: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 https://github.com/badrelmers/RefrEnv
REM https://stackoverflow.com/questions/171588/is-there-a-command-to-refresh-environment-variables-from-the-command-prompt-in-w

REM ___USAGE_____________________________________________________________
REM usage: 
REM        call refrenv.bat        full refresh. refresh all non critical variables*, and refresh the PATH

REM debug:
REM        to debug what this script do create this variable in your parent script like that
REM        set debugme=yes
REM        then the folder containing the files used to set the variables will be open. Then see
REM        _NewEnv.cmd this is the file which run inside your script to setup the new variables, you
REM        can also revise the intermediate files _NewEnv.cmd_temp_.cmd and _NewEnv.cmd_temp2_.cmd 
REM        (those two contains all the variables before removing the duplicates and the unwanted variables)


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 *critical variables: are variables which belong to cmd/windows and should not be refreshed normally like:
REM - windows vars:
REM 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


REM ___INFO_____________________________________________________________
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 This is a better alternative to the chocolatey refreshenv for cmd, which solves a lot of problems like:

REM The Chocolatey refreshenv is so bad if the variable have some cmd meta-characters, see this test:
    REM add this to the path in HKCU\Environment: test & echo baaaaaaaaaad, and run the chocolatey refreshenv you will see that it prints baaaaaaaaaad which is very bad, and the new path is not added to your path variable.
    REM This script solve this and you can test it with any meta-character, even something so bad like:
    REM ; & % ' ( ) ~ + @ # $ { } [ ] , ` ! ^ | > < \ / " : ? * = . - _ & echo baaaad
    
REM refreshenv adds only system and user environment variables, but CMD adds volatile variables too (HKCU\Volatile Environment). This script will merge all the three and remove any duplicates.

REM refreshenv reset your PATH. This script append the new path to the old path of the parent script which called this script. It is better than overwriting the old path, otherwise it will delete any newly added path by the parent script.

REM This script solve this problem described in a comment by @Gene Mayevsky: refreshenv 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.

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 volatile path. This script will merge all the three and remove any duplicates. this is what windows do by default too

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 characters: & % ' ( ) ~ + @ # $ { } [ ] ; , ` ! ^ | > < \ / " : ? * = . - _ & 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

if "%debugme%"=="yes" (
    echo RefrEnv - Refresh the Environment for CMD - ^(Debug enabled^)
) else (
    echo RefrEnv - Refresh the Environment for CMD
)

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 necessary 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 characters: ; & % ' ( ) ~ + @ # $ { } [ ] , ` ! ^ | > < \ / " : ? * = . - _ & 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")
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
    tmpF.WriteLine(sitem)
next
SystemPath = oEnvS("PATH")

set oEnvU=WshShell.Environment("User")
for each sitem in oEnvU
    tmpF.WriteLine(sitem)
next
UserPath = oEnvU("PATH")

set oEnvV=WshShell.Environment("Volatile")
for each sitem in oEnvV
    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(sitem)
REM next
REM here we add the actual session path, so we do not reset the original path, because maybe the parent script added some folders to the path, If we need to reset the path then comment the following line
ProcessPath = oEnvP("PATH")

REM merge System, User, Volatile, and process PATHs
NewPath = SystemPath & ";" & UserPath & ";" & VolatilePath & ";" & ProcessPath


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 compare 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 expand variables
NewPath = WshShell.ExpandEnvironmentStrings(NewPath)
REM remove duplicates
NewPath=remove_duplicates(NewPath)

REM remove_duplicates() will add a ; to the end so lets remove it if the last letter is ;
If Right(NewPath, 1) = ";" Then 
    NewPath = Left(NewPath, Len(NewPath) - 1) 
End If
  
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 should 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
        objTS.WriteLine strLine
    End If
Next

REM ____________________________________________________________
REM :: expand variables because registry save some variables as unexpanded %....%
REM :: and 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:

我不能在这里发布,我达到了发布限制,所以从这里下载

在bash中使用:source refresh .sh调用它

美国小妞Powershell:

从这里下载

在Powershell中使用:. .\refrenv.ps1调用它

其他回答

我做了一个更好的替代Chocolatey refreshenv cmd和cygwin,解决了很多问题,如:

The Chocolatey refreshenv is so bad if the variable have some cmd meta-characters, see this test: add this to the path in HKCU\Environment: test & echo baaaaaaaaaad, and run the chocolatey refreshenv you will see that it prints baaaaaaaaaad which is very bad, and the new path is not added to your path variable. This script solve this and you can test it with any meta-character, even something so bad like: ; & % ' ( ) ~ + @ # $ { } [ ] , ` ! ^ | > < \ / " : ? * = . - _ & echo baaaad refreshenv adds only system and user environment variables, but CMD adds volatile variables too (HKCU\Volatile Environment). This script will merge all the three and remove any duplicates. refreshenv reset your PATH. This script append the new path to the old path of the parent script which called this script. It is better than overwriting the old path, otherwise it will delete any newly added path by the parent script. This script solve this problem described in a comment here by @Gene Mayevsky: refreshenv 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.

我为cmd和cygwin/bash做了一个脚本, 你可以在这里找到他们: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 https://github.com/badrelmers/RefrEnv
REM https://stackoverflow.com/questions/171588/is-there-a-command-to-refresh-environment-variables-from-the-command-prompt-in-w

REM ___USAGE_____________________________________________________________
REM usage: 
REM        call refrenv.bat        full refresh. refresh all non critical variables*, and refresh the PATH

REM debug:
REM        to debug what this script do create this variable in your parent script like that
REM        set debugme=yes
REM        then the folder containing the files used to set the variables will be open. Then see
REM        _NewEnv.cmd this is the file which run inside your script to setup the new variables, you
REM        can also revise the intermediate files _NewEnv.cmd_temp_.cmd and _NewEnv.cmd_temp2_.cmd 
REM        (those two contains all the variables before removing the duplicates and the unwanted variables)


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 *critical variables: are variables which belong to cmd/windows and should not be refreshed normally like:
REM - windows vars:
REM 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


REM ___INFO_____________________________________________________________
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 This is a better alternative to the chocolatey refreshenv for cmd, which solves a lot of problems like:

REM The Chocolatey refreshenv is so bad if the variable have some cmd meta-characters, see this test:
    REM add this to the path in HKCU\Environment: test & echo baaaaaaaaaad, and run the chocolatey refreshenv you will see that it prints baaaaaaaaaad which is very bad, and the new path is not added to your path variable.
    REM This script solve this and you can test it with any meta-character, even something so bad like:
    REM ; & % ' ( ) ~ + @ # $ { } [ ] , ` ! ^ | > < \ / " : ? * = . - _ & echo baaaad
    
REM refreshenv adds only system and user environment variables, but CMD adds volatile variables too (HKCU\Volatile Environment). This script will merge all the three and remove any duplicates.

REM refreshenv reset your PATH. This script append the new path to the old path of the parent script which called this script. It is better than overwriting the old path, otherwise it will delete any newly added path by the parent script.

REM This script solve this problem described in a comment by @Gene Mayevsky: refreshenv 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.

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 volatile path. This script will merge all the three and remove any duplicates. this is what windows do by default too

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 characters: & % ' ( ) ~ + @ # $ { } [ ] ; , ` ! ^ | > < \ / " : ? * = . - _ & 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

if "%debugme%"=="yes" (
    echo RefrEnv - Refresh the Environment for CMD - ^(Debug enabled^)
) else (
    echo RefrEnv - Refresh the Environment for CMD
)

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 necessary 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 characters: ; & % ' ( ) ~ + @ # $ { } [ ] , ` ! ^ | > < \ / " : ? * = . - _ & 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")
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
    tmpF.WriteLine(sitem)
next
SystemPath = oEnvS("PATH")

set oEnvU=WshShell.Environment("User")
for each sitem in oEnvU
    tmpF.WriteLine(sitem)
next
UserPath = oEnvU("PATH")

set oEnvV=WshShell.Environment("Volatile")
for each sitem in oEnvV
    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(sitem)
REM next
REM here we add the actual session path, so we do not reset the original path, because maybe the parent script added some folders to the path, If we need to reset the path then comment the following line
ProcessPath = oEnvP("PATH")

REM merge System, User, Volatile, and process PATHs
NewPath = SystemPath & ";" & UserPath & ";" & VolatilePath & ";" & ProcessPath


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 compare 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 expand variables
NewPath = WshShell.ExpandEnvironmentStrings(NewPath)
REM remove duplicates
NewPath=remove_duplicates(NewPath)

REM remove_duplicates() will add a ; to the end so lets remove it if the last letter is ;
If Right(NewPath, 1) = ";" Then 
    NewPath = Left(NewPath, Len(NewPath) - 1) 
End If
  
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 should 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
        objTS.WriteLine strLine
    End If
Next

REM ____________________________________________________________
REM :: expand variables because registry save some variables as unexpanded %....%
REM :: and 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:

我不能在这里发布,我达到了发布限制,所以从这里下载

在bash中使用:source refresh .sh调用它

美国小妞Powershell:

从这里下载

在Powershell中使用:. .\refrenv.ps1调用它

我想到的最好的方法是只做一个Registry查询。下面是我的例子。

In my example I did an install using a Batch file that added new environment variables. I needed to do things with this as soon as the install was complete, but was unable to spawn a new process with those new variables. I tested spawning another explorer window and called back to cmd.exe and this worked but on Vista and Windows 7, Explorer only runs as a single instance and normally as the person logged in. This would fail with automation since I need my admin creds to do things regardless of running from local system or as an administrator on the box. The limitation to this is that it does not handle things like path, this only worked on simple enviroment variables. This allowed me to use a batch to get over to a directory (with spaces) and copy in files run .exes and etc. This was written today from may resources on stackoverflow.com

原批处理调用新批处理:

testenvget。cmd SDROOT(或任何变量)

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

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

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

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

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

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

还有另一种方法,我从各种不同的想法中想出的。请看下文。这基本上会得到最新的路径变量从注册表然而,这将导致一些问题,因为注册表查询将给出变量本身,这意味着到处都有一个变量,这将不会工作,所以为了解决这个问题,我基本上加倍的路径。非常讨厌的。更可取的方法是: 设置路径=%路径%;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.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的答案所记录的那样),并将整个环境表写入目标进程的内存中。

Edit: The size of the environment block is also stored in the _RTL_USER_PROCESS_PARAMETERS struct, but the memory is allocated on the process' heap. So from an external process we wouldn't have the ability to resize it and make it larger. I played around with using VirtualAllocEx to allocate additional memory in the target process for the environment storage, and was able to set and read an entirely new table. Unfortunately any attempt to modify the environment from normal means will crash and burn as the address no longer points to the heap (it will crash in RtlSizeHeap).

这适用于windows 7: SET PATH=%PATH%

通过输入echo %PATH%测试,它工作正常。也设置如果你打开一个新的cmd,不需要那些讨厌的重新启动:)

如果它只涉及一个(或几个)特定的变量,你想要改变,我认为最简单的方法是一个变通方法:在你的环境和当前的控制台会话中设置

Set将把变量放在当前会话中 SetX将把变量放在环境中,但不是在当前会话中

我使用这个简单的批处理脚本将Maven从Java7更改为Java8(两者都是env。批处理文件夹在我的PATH变量中,所以我总是可以调用'j8',在我的控制台和环境中,我的JAVA_HOME变量被改变:

j8.bat:

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

到目前为止,我发现这是最好的和最简单的工作。 你可能希望这是在一个命令中,但它只是没有在Windows…