ChatGPT解决这个技术问题 Extra ChatGPT

是否有从 Windows 的命令提示符刷新环境变量的命令?

如果我修改或添加环境变量,我必须重新启动命令提示符。有没有我可以执行的命令可以在不重新启动 CMD 的情况下执行此操作?

实际上,每个需要看到它们的程序都必须重新启动。环境在启动时被复制到进程的内存中,因此与系统定义的 envvar 不再有任何联系。
看完这些,我意识到现实世界中没有勺子;)你只需重新启动cmd。
不是命令,所以不是一个完整的答案,但如果我正确阅读以下内容,则使用 Win32 API 支持它:support.microsoft.com/en-us/help/104011/… 应该能够将该行编译成一个简单的 C 程序并在环境变量更新后运行它。
根据此线程,WM_SETTINGCHANGE(@CharlesGrunwald 提到的 win32 api)不适用于 cmd.exe 窗口:github.com/chocolatey/choco/issues/1589 - 这是他们编写 refreshenv 命令的原因

M
MarredCheese

在 Windows 7/8/10 上,您可以安装 Chocolatey,它有一个内置脚本。

安装 Chocolatey 后,只需键入 refreshenv


这是有效的答案,很高兴听到投反对票的人的来信
我究竟做错了什么? $>refreshenv 'refreshenv' 不是内部或外部命令、可运行程序或批处理文件。
@aclokay 不确定。请提供有关您的系统配置的更多详细信息以进行调试。同时,您可以在此处参考类似的未解决问题。 github.com/chocolatey/choco/issues/250
它不能开箱即用。你必须已经安装了 Chocolatey,它会安装 refreshenv 脚本。说“在 Windows 7/8/10 中”具有误导性
如果 refreshenv 过早地存在您的脚本(就像它为我所做的那样),您可以改用“调用 RefreshEnv.cmd”。 (见github.com/chocolatey/choco/issues/1461
V
Vishrant

可以用vbs脚本捕获系统环境变量,但是需要bat脚本才能真正改变当前的环境变量,所以这是一个组合解决方案。

创建一个包含此代码的名为 resetvars.vbs 的文件,并将其保存在路径中:

Set oShell = WScript.CreateObject("WScript.Shell")
filename = oShell.ExpandEnvironmentStrings("%TEMP%\resetvars.bat")
Set objFileSystem = CreateObject("Scripting.fileSystemObject")
Set oFile = objFileSystem.CreateTextFile(filename, TRUE)

set oEnv=oShell.Environment("System")
for each sitem in oEnv 
    oFile.WriteLine("SET " & sitem)
next
path = oEnv("PATH")

set oEnv=oShell.Environment("User")
for each sitem in oEnv 
    oFile.WriteLine("SET " & sitem)
next

path = path & ";" & oEnv("PATH")
oFile.WriteLine("SET PATH=" & path)
oFile.Close

创建另一个包含此代码的文件名 resetvars.bat,位置相同:

@echo off
%~dp0resetvars.vbs
call "%TEMP%\resetvars.bat"

当您想刷新环境变量时,只需运行 resetvars.bat

道歉:

我提出这个解决方案的两个主要问题是

一个。我找不到将环境变量从 vbs 脚本导出回命令提示符的简单方法,并且

湾。 PATH 环境变量是用户和系统 PATH 变量的串联。

我不确定用户和系统之间的变量冲突的一般规则是什么,所以我选择让用户覆盖系统,除了专门处理的 PATH 变量。

我使用怪异的vbs+bat+临时bat机制来解决从vbs导出变量的问题。

注意:此脚本不会删除变量。

这大概可以改进。

添加

如果您需要将环境从一个 cmd 窗口导出到另一个,请使用此脚本(我们称之为 exportvars.vbs):

Set oShell = WScript.CreateObject("WScript.Shell")
filename = oShell.ExpandEnvironmentStrings("%TEMP%\resetvars.bat")
Set objFileSystem = CreateObject("Scripting.fileSystemObject")
Set oFile = objFileSystem.CreateTextFile(filename, TRUE)

set oEnv=oShell.Environment("Process")
for each sitem in oEnv 
    oFile.WriteLine("SET " & sitem)
next
oFile.Close

在要导出的窗口中运行 exportvars.vbs,然后切换到要导出的窗口,然后键入:

"%TEMP%\resetvars.bat"

也许您可以使用 FOR /F "tokens=1,*" %%c IN ('resetvars.vbs') DO 结构来避免临时文件
正如我在回答中所说“或者,在现有命令提示符下使用 SET 手动添加”。这就是它正在有效地做的事情。不过答案很好。
@itsadok - 鉴于这是现在公认的答案,您应该在开始时添加一个简短的解释,以便将脚本置于上下文中。即指出,如果不按上述方式手动更新或重新启动 cmd.exe,就无法将 env var 更改传播到打开的 cmd.exe。
@Keyslinger:这实际上是不可能的。生成的任何程序都可以更新自己的环境,但不能更新正在运行的 cmd.exe 实例的环境。批处理文件可以更新 cmd.exe 环境,因为它在 cmd.exe 的同一实例中运行。
请注意,某些程序也会缓存环境变量。就我而言,我运行了这个脚本,然后不得不重新启动 Intellij。
N
NateS

这是 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 "RefreshEnv.cmd only works from cmd.exe, please install the Chocolatey Profile to take advantage of refreshenv from PowerShell"
echo | set /p dummy="Refreshing environment variables from registry for cmd.exe. 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

    :: capture user / architecture
    SET "OriginalUserName=%USERNAME%"
    SET "OriginalArchitecture=%PROCESSOR_ARCHITECTURE%"

    :: Set these variables
    call "%TEMP%\_env.cmd"

    :: Cleanup
    del /f /q "%TEMP%\_env.cmd" 2>nul

    :: reset user / architecture
    SET "USERNAME=%OriginalUserName%"
    SET "PROCESSOR_ARCHITECTURE=%OriginalArchitecture%"

    echo | set /p dummy="Finished."
    echo .

+1 如果您安装了 Chocolatey,您只需运行 RefreshEnv 即可在当前会话中获取更新的环境变量。
这是一个非常有用的实用软件,非常感谢分享。
注意:Chocolatey 已移动存储库,可以在此处找到此脚本的最新版本(修复了一些错误):github.com/chocolatey/choco/blob/master/src/…
这也应该从 Powershell 工作吗?它似乎只适用于我的 cmd.exe
在 cmd/gitbash/PowerShell 中,这似乎对我根本不起作用
C
Community

按照设计,Windows 没有内置机制将环境变量添加/更改/删除传播到已经运行的 cmd.exe,无论是从另一个 cmd.exe 还是从“我的电脑 -> 属性 -> 高级设置 ->环境变量”。

如果在现有打开的命令提示符范围之外修改或添加新环境变量,则需要重新启动命令提示符,或者在现有命令提示符中使用 SET 手动添加。

latest accepted answer 显示了通过手动刷新脚本中的所有环境变量的部分解决方法。该脚本处理在“我的电脑...环境变量”中全局更改环境变量的用例,但如果在一个 cmd.exe 中更改了环境变量,该脚本将不会将其传播到另一个正在运行的 cmd.exe。


这不应该只是因为它没有回答所提出的问题而被接受的答案。在找到答案之前,这个问题应该没有公认的答案。
令人讨厌的是,额外的 cmd.exe 实例不算在内。在更改反映在任何新的 cmd.exe 中之前,它们都必须被杀死。
这个答案的负面评论和向下标记显示了堆栈溢出有时是如何破坏的。凯夫给出了正确的答案。仅仅因为你不喜欢它没有理由标记它。
Kev 确实回答了这个问题。问题是没有内置的解决方案。
关于@Mike F 的评论,在 Windows 7 中似乎并非如此。
C
Community

在最终找到更简单的解决方案之前,我遇到了这个答案。

只需在任务管理器中重新启动 explorer.exe

我没有测试,但您可能还需要重新打开命令提示符。

归功于此处的 Timo HuovinenNode not recognized although successfully installed(如果这对您有所帮助,请给这个人的评论信用)。


该解决方案在 Windows 10 中对我有帮助
问题是:“有没有我可以执行的命令可以在不重新启动 CMD 的情况下执行此操作?”
好的,从任务管理器我无法重新启动 explorer.exe,只能完成他。我做到了,但我的任务栏已经坏了。启动 explorer;exe 非常简单。让我们“Ctrl + shift + escape”-> 文件--> “执行新任务”-> “explorer.exe” 为我完成了这项工作。是的,毕竟 env var 已在新的 cmd 窗口中使用。谢谢大家
很好的解决方法,谢谢!要扩展和解决 @Oscar 的评论:以管理员身份启动 cmd 窗口。使用命令 taskkill /f /im explorer.exe && explorer.exe。这将杀死 explorer.exe 进程并重新启动它。
@S3DEV ...这是具有内置功能的实际答案。
s
shanethehat

这适用于 Windows 7:SET PATH=%PATH%;C:\CmdShortcuts

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


不适用于我的“新 cmd”(Win7 x64)。请参阅screenvideo
这并不能解决所提出的问题,也不应该解决。最初的问题是如何将环境变量刷新为已在该终端之外设置的值。
虽然这不能回答问题,但它提供了一半的最佳工作解决方案。我使用它——对于我设置的任何变量——然后我打开控制面板并全局添加环境变量。我不喜欢使用 setx,因为它继承了当前环境,其中可能包含已修改的变量,而不是我想要的永久变量。这样做可以让我避免重新启动控制台以使用变量,同时避免将来无法在全局范围内使用它们的问题。
J
Jens A. Koch

使用“setx”并重新启动 cmd 提示符

该作业有一个名为“setx”的命令行工具。它用于读取和写入 env 变量。命令窗口关闭后,变量仍然存在。

它“在用户或系统环境中创建或修改环境变量,无需编程或编写脚本。setx 命令还检索注册表项的值并将它们写入文本文件。”

注意:此工具创建或修改的变量将在未来的命令窗口中可用,但在当前的 CMD.exe 命令窗口中不可用。所以,你必须重新开始。

如果缺少 setx

http://download.microsoft.com/download/win2000platform/setx/1.00.0.1/nt5/en-us/setx_setup.exe

或者修改注册表

MSDN 说:

要以编程方式添加或修改系统环境变量,请将它们添加到 HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment 注册表项,然后广播 WM_SETTINGCHANGE 消息,并将 lParam 设置为字符串“Environment”。这允许应用程序(例如 shell)获取您的更新。


您能否详细说明如何使用 setx 读取环境变量?我已经浏览了各种文档,但我只是没有看到它。 :-/
setx VARIABLE -k "HKEY_LOCAL_MACHINE\Software\Microsoft\WindowsNT\CurrentVersion\CurrentVersion" echo %VARIABLE%
当前系统环境:HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment\VARIABLE 当前用户环境:HKEY_CURRENT_USER\Environment\VARIABLE
设置/?在注释中说:“在本地系统上,此工具创建或修改的变量将在未来的命令窗口中可用,但在当前的 CMD.exe 命令窗口中不可用。” OP想更新当前的cmd。
买家小心!如果您有一个特别长的 %PATH%,那么 setx 可能会将其截断为 1024 字节! 就这样,他的夜晚消失了
S
Sridhar Ratnakumar

调用此函数对我有用:

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

并不是所有的程序都听这个消息(事实上他们中的大多数可能没有)
不,它也适用于非 GUI 程序。至于监听程序......如果确保重新启动的程序将接收更新的环境,那么这个问题就给了你。
C
Christopher Holmes

我想出的最好的方法是只做一个注册表查询。这是我的例子。

在我的示例中,我使用添加了新环境变量的批处理文件进行了安装。安装完成后,我需要立即执行此操作,但无法使用这些新变量生成新进程。我测试了生成另一个资源管理器窗口并回调到 cmd.exe 并且这有效,但在 Vista 和 Windows 7 上,资源管理器仅作为单个实例运行,并且通常在登录的人时运行。这会因自动化而失败,因为我需要我的管理员凭据无论是从本地系统运行还是以管理员身份在盒子上运行。对此的限制是它不处理路径之类的东西,这只适用于简单的环境变量。这使我可以使用批处理来访问目录(带有空格)并复制运行 .exes 等文件。这是今天从 stackoverflow.com 上的可能资源编写的

原始批次调用新批次:

testenvget.cmd SDROOT(或任何变量)

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

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

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

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

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

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

还有另一种方法是我从各种不同的想法中提出的。请看下文。这基本上会从注册表中获取最新的路径变量,但是,这会导致一些问题,因为注册表查询本身会给出变量,这意味着任何地方都有一个变量不起作用,所以为了解决这个问题,我基本上是双倍的路径。非常讨厌。更优选的方法是: Set Path=%Path%;C:\Program Files\Software....\

不管这里是新的批处理文件,请谨慎使用。

@ECHO OFF
SETLOCAL ENABLEEXTENSIONS
set org=%PATH%
for /f "tokens=2*" %%A in ('REG QUERY "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" /v Path ^|FIND /I "Path"') DO (
SET path=%%B
)
SET PATH=%org%;%PATH%
set path

M
Moses Davidowitz

重新启动资源管理器为我做了这个,但只适用于新的 cmd 终端。

我设置路径的终端已经可以看到新的 Path 变量(在 Windows 7 中)。

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

最简单的方法。谢谢你。
这应该是公认的答案。
j
josh poley

可以通过覆盖指定进程本身内的环境表来做到这一点。

作为概念证明,我编写了这个示例应用程序,它只是在 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 _PEBdt _RTL_USER_PROCESS_PARAMETERS 中)

要将概念验证更改为 OP 所需的内容,它只需枚举当前系统和用户环境变量(例如 @tsadok 的回答所记录的)并将整个环境表写入目标进程的内存。

编辑:环境块的大小也存储在 _RTL_USER_PROCESS_PARAMETERS 结构中,但内存是在进程的堆上分配的。因此,从外部进程我们将无法调整它的大小并使其变大。我尝试使用 VirtualAllocEx 在目标进程中为环境存储分配额外的内存,并且能够设置和读取一个全新的表。不幸的是,任何从正常方式修改环境的尝试都会崩溃并烧毁,因为地址不再指向堆(它将在 RtlSizeHeap 中崩溃)。


P
Paul Roub

在不为当前会话重新启动的情况下将变量添加到路径的最简单方法是打开命令提示符并键入:

PATH=(VARIABLE);%path%

然后按回车。

要检查您的变量是否已加载,请键入

PATH

然后按回车。但是,在您重新启动之前,该变量将只是路径的一部分。


对我不起作用,无法访问路径变量中的二进制文件
A
Algonaut

环境变量保存在 HKEY_LOCAL_MACHINE\SYSTEM\ControlSet\Control\Session Manager\Environment 中。

许多有用的环境变量(例如 Path)存储为 REG_SZ。有几种方法可以访问注册表,包括 REGEDIT:

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

输出以幻数开头。因此,要使用 find 命令搜索它,需要键入并重定向它:type <filename> | findstr -c:\"Path\"

因此,如果您只想使用系统属性中的内容刷新当前命令会话中的路径变量,则以下批处理脚本可以正常工作:

刷新路径.cmd:

@echo off

    REM This solution requests elevation in order to read from the registry.

    if exist %temp%\env.reg del %temp%\env.reg /q /f

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

    if not exist %temp%\env.reg (
       echo "Unable to write registry to temp location"
       exit 1
       )

    SETLOCAL EnableDelayedExpansion

    for /f "tokens=1,2* delims==" %%i in ('type %temp%\env.reg ^| findstr -c:\"Path\"=') do (
       set upath=%%~j
       echo !upath:\\=\! >%temp%\newpath
       )

     ENDLOCAL

     for /f "tokens=*" %%i in (%temp%\newpath) do set path=%%i

环境变量保存在注册表中。保存在注册表中的是一个模板,Windows Explorer 等程序从该模板(重新)构造它们的环境变量when notified to do so。实际的环境变量是每个进程的,并且存储在每个进程自己的地址空间中,最初是从其父进程继承的,之后可以根据进程的突发奇想进行修改。
e
estebro

尝试以管理员身份打开新的命令提示符。这在 Windows 10 上对我有用。(我知道这是一个旧答案,但我不得不分享这个,因为必须为此编写 VBS 脚本是荒谬的)。


“新命令” - 是的,但为什么是管理员?
@SamGinrich 回到当我测试这个时,我相信我没有看到我的环境变量的更改通过简单地打开一个新的命令提示符可靠地传播。以管理员身份打开新的命令提示符似乎是可靠的。
我的经验是,当您更改 windows-settings 中的变量时,它们会立即在任何新打开的命令行窗口中可见。
D
Daniel Fensterheim

令人困惑的事情可能是有几个地方可以启动 cmd。在我的情况下,我从 Windows 资源管理器运行 cmd 并且环境变量没有改变,而从“运行”(Windows 键 + r)启动 cmd 时,环境变量发生了变化。

就我而言,我只需要从任务管理器中终止 Windows 资源管理器进程,然后从任务管理器中重新启动它。

一旦我这样做了,我就可以从 Windows 资源管理器生成的 cmd 访问新的环境变量。


S
Sebastian

我在批处理脚本中使用以下代码:

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

通过在 SETX 之后使用 SET,可以直接使用“本地”变量,而无需重新启动命令窗口。在下一次运行时,将使用环境变量。


虽然我得到了你所做的,但很可能他想要一些用于并行脚本的东西,一个脚本设置全局变量,而另一个脚本读取它们。否则,涉及 setx 没有意义,set 就足够了。
J
Jeroen van Dijk-Jun

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

Set 会将 var 放入当前会话中

SetX 会将 var 放入环境中,但不在您当前的会话中

我有这个简单的批处理脚本来将我的 Maven 从 Java7 更改为 Java8(它们都是 env.vars)批处理文件夹在我的 PATH var 中,所以我总是可以在我的控制台和环境中调用 'j8' 和我的 JAVA_HOME var变了:

j8.bat:

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

到目前为止,我发现这个工作最好,最简单。您可能希望它在一个命令中,但它根本不存在于 Windows 中......


最好的选择,IMO。 ;)
C
Charles Grunwald

我已经使用了几年的解决方案:

@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

编辑:糟糕,这是更新版本。


我喜欢你的回答。在此处 stackoverflow.com/q/61473551/1082063 对我的问题发布相同的答案,我将接受它作为 the 答案。谢谢。
B
Badr Elmers

我为 cmd 和 cygwin 制作了 Chocolatey refreshenv 的更好替代品,它解决了很多问题,例如:

如果变量有一些 cmd 元字符,Chocolatey refreshenv 非常糟糕,请参阅此测试:将其添加到 HKCU\Environment 中的路径:test & echo baaaaaaaaaad,然后运行 Chocolatey refreshenv 你会看到它打印出 baaaaaaaaaad,这非常不好,并且新路径不会添加到您的路径变量中。这个脚本解决了这个问题,你可以用任何元字符来测试它,甚至像这样糟糕的东西:; &%'()~+@#${}[],`! ^ | > < \ / " : ? * = . - _ & echo baaaad

refreshenv 仅添加系统和用户环境变量,但 CMD 也添加了 volatile 变量(HKCU\Volatile Environment)。此脚本将合并所有三个并删除所有重复项。

refreshenv 重置您的 PATH。此脚本将新路径附加到调用此脚本的父脚本的旧路径。这比覆盖旧路径要好,否则它将删除父脚本新添加的任何路径。

此脚本解决了@Gene Mayevsky 在此处的评论中描述的此问题:refreshenv 修改了环境变量 TEMP 和 TMP,将它们替换为存储在 HKCU\Environment 中的值。在我的情况下,我运行脚本来更新由 Jenkins 作业修改的环境变量,该变量在 SYSTEM 帐户下运行的从属设备上运行,因此 TEMP 和 TMP 被 %USERPROFILE%\AppData\Local\Temp 代替,而不是 C:\Windows\Temp。这会破坏构建,因为链接器无法打开系统配置文件的 Temp 文件夹。

我为 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:

我无法在此处发布我已达到发布限制,因此请从 here 下载

从 bash 调用它:source refrenv.sh

对于 Powershell:

here 下载

从 Powershell 调用它:. .\refrenv.ps1


嗨,我在 Git bash 中试过这个,但它对我不起作用。这里有一个 image 这对 git bash 终端不起作用?它应该是一个 bash 终端 MinGW
我刚刚测试了 Git bash,它对我来说很好用,请参阅 imgur.com/sQS84QI – 请在 github 中打开一个问题,以便我们讨论这个问题,看看它为什么不适合你:github.com/badrelmers/RefrEnv/issues
D
DieterDP

我喜欢巧克力遵循的方法,正如匿名懦夫的回答中所发布的那样,因为它是一种纯粹的批处理方法。但是,它会留下一个临时文件和一些临时变量。我为自己制作了一个更干净的版本。

在您的 PATH 上的某处创建一个文件 refreshEnv.bat。通过执行 refreshEnv 刷新您的控制台环境。

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

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

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

ECHO Unknown command: %1
EXIT /b 1 

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

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

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

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

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

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

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

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

ECHO Environment successfully refreshed.

这也适用于 %CLIENTNAME% 吗? - 不适合我 - stackoverflow.com/questions/37550160/…
%CLIENTNAME% 在我的环境中不可用,通过阅读您的问题,我会假设它是由外部过程设置的。 (当一个进程启动一个子进程时,它能够为该子进程调整环境。)由于它不是实际环境变量的一部分,因此该脚本不会对其进行更新。
嗨@DieterDP,您的解决方案对我有用!我在 64 位机器上使用 Windows 10。我确实收到错误消息:“错误:系统无法找到指定的注册表项或值。”。尽管如此,环境变量的更新是成功的。错误来自哪里?
如果不亲自进行实际测试,很难说,但我猜 W10 上的注册表结构可能会略有不同。如果您愿意,请尝试通过在命令行上执行命令来查找错误。
A
Andy McRae

感谢您发布这个非常有趣的问题,即使在 2019 年(确实,更新 shell cmd 并不容易,因为它是上面提到的单个实例),因为在 windows 中更新环境变量可以完成许多自动化任务而无需必须手动重新启动命令行。

例如,我们使用它来允许在我们定期重新安装的大量机器上部署和配置软件。而且我必须承认,在我们的软件部署期间必须重新启动命令行是非常不切实际的,并且需要我们找到不一定令人愉快的解决方法。让我们来解决我们的问题。我们进行如下。

1 - 我们有一个批处理脚本,它依次调用这样的 powershell 脚本

[文件:task.cmd]。

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

- 此后,refresh.ps1 脚本使用注册表项(GetValueNames() 等)更新环境变量。然后,在同一个 powershell 脚本中,我们只需要调用可用的新环境变量。例如,在典型情况下,如果我们之前使用 cmd 使用静默命令安装了 nodeJS,则在调用该函数后,我们可以直接调用 npm 在同一会话中安装特定的包,如下所示。

[文件:刷新.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 相同的命令。


P
PhiLho

正如凯夫所说,没有直接的方法。在大多数情况下,生成另一个 CMD 框更简单。更烦人的是,正在运行的程序也不知道更改(尽管 IIRC 可能有一个广播消息要观看以通知此类更改)。

更糟糕的是:在旧版本的 Windows 中,您必须注销然后重新登录以考虑更改...


n
noname

我使用这个 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%"

w
wardies

编辑:这仅在您正在执行的环境更改是由于运行批处理文件时才有效。

如果批处理文件以 SETLOCAL 开头,那么即使您忘记在批处理退出之前调用 ENDLOCAL 或意外中止,它也会在退出时解开回您的原始环境。

我编写的几乎每个批处理文件都以 SETLOCAL 开头,因为在大多数情况下,我不希望环境更改的副作用仍然存在。如果我确实希望某些环境变量更改传播到批处理文件之外,那么我的最后一个 ENDLOCAL 看起来像这样:

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

J
Jens Hykkelbjerg

为了解决这个问题,我使用 setx 和 set 更改了环境变量,然后重新启动了 explorer.exe 的所有实例。这样,随后启动的任何进程都将具有新的环境变量。

我的批处理脚本执行此操作:

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

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

这种方法的问题是当前打开的所有资源管理器窗口都将关闭,这可能是一个坏主意 - 但请参阅 Kev 的帖子以了解为什么这是必要的


A
Amey Mahajan

我只是想说明,那些使用 Anaconda 的人,当你使用 Chocolatey Refreshenv 命令时;所有与 conda 相关的环境变量都将丢失。为了解决这个问题,最好的方法是重新启动 CMD。 :(