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


479

如果修改或添加环境变量,则必须重新启动命令提示符。我是否可以执行一个无需重启CMD即可执行的命令?


38
实际上,每个需要查看它们的程序都必须重新启动。该环境在启动时被复制到进程的内存中,因此不再与系统定义的envvar建立任何连接。
乔伊(Joey)

15
阅读这些内容后,我意识到在现实世界中没有汤匙 ;),只需重新启动cmd。
n611x007

不是命令,不是一个答案,但是如果我正确阅读以下内容,则使用Win32 API对此有支持:support.microsoft.com/en-us/help/104011/…Shoud能够将该行编译为简单的C程序,并在环境变量更新后运行它。
查尔斯·格伦沃尔德'18

WM_SETTINGCHANGE(@CharlesGrunwald提到的win32 api)不适用于以下线程的cmd.exe窗口:github.com/chocolatey/choco/issues/1589-这是他们编写
refreshenv

Answers:


137

您可以使用vbs脚本捕获系统环境变量,但实际上需要一个bat脚本来更改当前环境变量,因此这是一个组合解决方案。

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

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

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

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

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

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

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

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


道歉

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

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

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

我不确定用户和系统之间的变量冲突的一般规则是什么,因此我选择使用户覆盖系统,但专门处理的PATH变量除外。

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

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

这可能可以改善。

添加

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

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

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

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

"%TEMP%\resetvars.bat"

2
也许您可以使用FOR / F“ tokens = 1,*” %% c IN('resetvars.vbs')DO构造来避免使用临时文件
tzot

2
正如我在答案中所说的那样:“或者,在现有命令提示符下使用SET手动添加。” 这就是有效的做法。很好的答案。
凯夫

2
@itsadok-既然现在已经可以接受这个答案了,那么您应该在开始时添加简短的说明,以将脚本置于上下文中。即指出,如果没有如上所述的手动更新或通过重新启动cmd.exe,则不可能将环境变量传播到打开的cmd.exe。
凯夫(Kev)

该脚本处理在“我的电脑...环境变量”中全局更改环境变量的用例,但是如果在一个cmd.exe中更改了环境变量,该脚本将不会将其传播到另一个我认为是运行的cmd.exe。可能是常见的情况。
凯夫(Kev)

1
@Keyslinger:这实际上是不可能的。产生的任何程序都可以更新自己的环境,但不能更新正在运行的cmd.exe实例的环境。批处理文件可以在cmd.exe的同一实例中运行,因此可以更新cmd.exe环境。
Ben Voigt 2014年

112

这是Chocolatey的用途。

https://github.com/chocolatey/choco/blob/master/src/chocolatey.resources/redirects/RefreshEnv.cmd

@echo off
::
:: RefreshEnv.cmd
::
:: Batch file to read environment variables from registry and
:: set session variables to these values.
::
:: With this batch file, there should be no need to reload command
:: environment every time you want environment changes to propagate

echo | set /p dummy="Reading environment variables from registry. Please wait... "

goto main

:: Set one environment variable from registry key
:SetFromReg
    "%WinDir%\System32\Reg" QUERY "%~1" /v "%~2" > "%TEMP%\_envset.tmp" 2>NUL
    for /f "usebackq skip=2 tokens=2,*" %%A IN ("%TEMP%\_envset.tmp") do (
        echo/set %~3=%%B
    )
    goto :EOF

:: Get a list of environment variables from registry
:GetRegEnv
    "%WinDir%\System32\Reg" QUERY "%~1" > "%TEMP%\_envget.tmp"
    for /f "usebackq skip=2" %%A IN ("%TEMP%\_envget.tmp") do (
        if /I not "%%~A"=="Path" (
            call :SetFromReg "%~1" "%%~A" "%%~A"
        )
    )
    goto :EOF

:main
    echo/@echo off >"%TEMP%\_env.cmd"

    :: Slowly generating final file
    call :GetRegEnv "HKLM\System\CurrentControlSet\Control\Session Manager\Environment" >> "%TEMP%\_env.cmd"
    call :GetRegEnv "HKCU\Environment">>"%TEMP%\_env.cmd" >> "%TEMP%\_env.cmd"

    :: Special handling for PATH - mix both User and System
    call :SetFromReg "HKLM\System\CurrentControlSet\Control\Session Manager\Environment" Path Path_HKLM >> "%TEMP%\_env.cmd"
    call :SetFromReg "HKCU\Environment" Path Path_HKCU >> "%TEMP%\_env.cmd"

    :: Caution: do not insert space-chars before >> redirection sign
    echo/set Path=%%Path_HKLM%%;%%Path_HKCU%% >> "%TEMP%\_env.cmd"

    :: Cleanup
    del /f /q "%TEMP%\_envset.tmp" 2>nul
    del /f /q "%TEMP%\_envget.tmp" 2>nul

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

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

65
+1如果您安装了Chocolatey,则可以运行RefreshEnv以将更新的环境变量添加到当前会话中。
马丁·瓦尔古尔

2
这是一个非常有用的实用程序,非常感谢您的共享。
Sabuncu

10
注:的巧克力移动回购和最新版本的脚本可以在这里找到的(有一些bug修复):github.com/chocolatey/choco/blob/master/src/...
迈克尔·伯尔

1
这个Powershell也应该做吗?它似乎只cmd.exe对我有用。
craq

1
它在PowerShell @craq中对我有用。运行Windows10 x64。
mazunki

100

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

安装Chocolatey之后,只需键入refreshenv


这是有效的答案,很高兴听到反对的人
the_joric

2
我究竟做错了什么?$> refreshenv'refreshenv'不被识别为内部或外部命令,可操作程序或批处理文件。
Aclokay

@aclokay不确定。请提供有关您要调试的系统配置的更多详细信息。同时,您可以在此处参考类似的未解决问题。github.com/chocolatey/choco/issues/250
欢乐

这对我也不起作用。我使用的是W7专业版,也许它只能在更完整的版本中使用。
alseether

1
如果refreshenv过早存在(如对我一样),则可以使用“调用RefreshEnv.cmd”。(见github.com/chocolatey/choco/issues/1461
sfiss

59

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

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

最新接受的答案显示了部分工作由各地手动刷新所有的环境变量的脚本。该脚本处理在“我的电脑...环境变量”中全局更改环境变量的用例,但是如果在一个cmd.exe中更改了环境变量,该脚本将不会将其传播到另一个正在运行的cmd.exe。


如果有人对这个问题有有效的解决方案,我会定期检查,并可能更改接受的答案。
Eric Sc​​hoonover,

1
这不应该仅仅是因为它不能回答所提出的问题而不能被接受。在找到一个问题之前,这个问题应该一直没有被接受的答案。
shoosh

4
令人讨厌的是,cmd.exe的额外实例不计算在内。在所有新cmd.exe反映更改之前,必须全部杀死它们。

6
否定评论和该答案的向下标记显示了有时堆栈中断是如何发生的。凯夫给出了正确的答案。只是因为您不喜欢它,所以没有理由将其标记下来。
David Arno,

Kev确实回答了这个问题。问题是没有内置的解决方案。
Eric Sc​​hoonover,

40

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

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

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

归功于 蒂莫Huovinen这里:节点无法识别,虽然安装成功(如果这帮助了你,请去给这人的评论信用)。


我到达这里是因为我试图向Visual Studio添加一个外部工具,因此我可以在解决方案的根目录下打开命令提示符,如本博客中所述: neverindoubtnet.blogspot.com/2012/10/… ...和我有类似的问题……我试图让“ git”出现在我的path变量中。我将git目录添加到PATH变量中,但是它们不会出现在从Visual Studio打开的命令提示符中。简单的解决方案是重新启动Visual Studio。然后,在cmd中可以看到对PATH变量的新添加。
David Barrows

4
该解决方案可以帮助我在Windows 10
ganchito55

7
问题是:“是否存在我可以执行的命令,而无需重启CMD即可执行此命令?”
Florian F

从任务管理器中可以,我无法重新启动explorer.exe,只能完成他。我做到了,但是我的任务栏被打破了。要启动explorer; exe,它非常简单。让我们“ Ctrl + Shift + Escape”->文件->“执行新任务”->“ explorer.exe”为我完成了工作。是的,毕竟在新的cmd窗口中使用了env var。感谢所有人
奥斯卡

好的解决方案,谢谢!展开并解决@Oscar的评论:cmd以管理员身份启动一个窗口。使用命令taskkill /f /im explorer.exe && explorer.exe。这将终止explorer.exe进程并重新启动它。
S3DEV

32

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

通过输入echo%PATH%进行测试,效果很好。如果您打开新的cmd,也可以设置,不再需要烦人的重启:)


1
对我而言,不适用于“新cmd”(Win7 x64)。观看屏幕录像
Igor,

26
这不能解决所问的问题,也不能解决。最初的问题是如何将环境变量刷新为该终端以外的值。
csauve 2015年

尽管这不能解决问题,但它提供了最佳工作解决方案的一半。我用它-无论我要设置什么变量-然后我打开控制面板并全局添加环境变量。我不喜欢使用setx它,因为它继承了当前环境,该环境可能具有已修改的变量,而不是我想要的永久变量。通过这种方式,我避免了重新启动控制台以使用变量,同时避免了将来无法在全局范围内使用它们的问题。
dgo

25

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

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

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

注意:此工具创建或修改的变量将在以后的命令窗口中可用,但在当前的CMD.exe命令窗口中不可用。因此,您必须重新启动。

如果setx缺少:


或修改注册表

MSDN说:

要以编程方式添加或修改系统环境变量,请将它们添加到 HKEY_LOCAL_MACHINE \ System \ CurrentControlSet \ Control \ Session Manager \ Environment注册表项中,然后广播将lParam设置为字符串“ Environment ” 的WM_SETTINGCHANGE 消息。

这使应用程序(例如外壳程序)可以获取您的更新。


1
您能扩展一下如何使用setx读取环境变量吗?我浏览过各种文档,但我只是没有看到它。:-/
Mark Ribau 2012年

2
setx变量-k“ HKEY_LOCAL_MACHINE \ Software \ Microsoft \ WindowsNT \ CurrentVersion \ CurrentVersion” echo%VARIABLE%
Jens A. Koch 2012年

3
当前系统环境:HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment\VARIABLE当前用户环境: HKEY_CURRENT_USER\Environment\VARIABLE
Mark Ribau 2012年

5
setx /?在注释中说:“在本地系统上,此工具创建或修改的变量将在以后的命令窗口中可用,但在当前的 CMD.exe命令窗口中不可用。” OP希望更新当前的cmd。
Superole 2014年

4
买家当心!如果您特别长,%PATH%setx可能会将其截断为1024个字节!就这样,他的夜晚消失了
FaCE

15

调用此功能对我有用:

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

8
并非所有程序都收听此消息(实际上,大多数程序可能没有)
Rehan Khwaja

不,它也适用于非GUI程序。至于侦听程序...问题,如果要确保重新启动的程序将接收更新的环境,这将为您提供解决方案。
user2023370

11

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

在我的示例中,我使用添加了新环境变量的批处理文件进行了安装。安装完成后,我需要立即执行此操作,但是无法使用这些新变量生成新进程。我测试了生成另一个浏览器窗口并调用cmd.exe的方法,该方法可以正常工作,但在Vista和Windows 7上,资源管理器仅作为单个实例运行,并且通常以登录用户身份运行。由于我需要管理员权限才能自动执行操作,因此失败无论从本地系统运行还是以管理员身份运行,都可以执行此操作。这样做的局限性在于它不能处理路径之类的问题,只能用于简单的环境变量。这使我可以使用批处理转到目录(包含空格)并复制运行.exes等文件。这是今天从stackoverflow.com上的may资源编写的

对新批次的原始批次调用:

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

我还从各种不同的想法中想到了另一种方法。请看下面。基本上,这将从注册表中获取最新的路径变量,但是,这将导致许多问题,因为注册表查询本身将提供变量,这意味着到处都有变量将不起作用,因此请解决这个问题基本上使路径加倍。真讨厌 更合适的方法是:设置路径=%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

8

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

PATH=(VARIABLE);%path%

然后按enter

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

PATH

然后按enter。但是,在重新启动之前,变量将仅是路径的一部分。


不适用于我,无法访问路径变量中的二进制文件
user2305193

7

为此,可以通过覆盖指定进程本身中的环境表来实现。

作为概念证明,我编写了此示例应用程序,该应用程序仅在cmd.exe进程中编辑了一个(已知)环境变量:

typedef DWORD (__stdcall *NtQueryInformationProcessPtr)(HANDLE, DWORD, PVOID, ULONG, PULONG);

int __cdecl main(int argc, char* argv[])
{
    HMODULE hNtDll = GetModuleHandleA("ntdll.dll");
    NtQueryInformationProcessPtr NtQueryInformationProcess = (NtQueryInformationProcessPtr)GetProcAddress(hNtDll, "NtQueryInformationProcess");

    int processId = atoi(argv[1]);
    printf("Target PID: %u\n", processId);

    // open the process with read+write access
    HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION, 0, processId);
    if(hProcess == NULL)
    {
        printf("Error opening process (%u)\n", GetLastError());
        return 0;
    }

    // find the location of the PEB
    PROCESS_BASIC_INFORMATION pbi = {0};
    NTSTATUS status = NtQueryInformationProcess(hProcess, ProcessBasicInformation, &pbi, sizeof(pbi), NULL);
    if(status != 0)
    {
        printf("Error ProcessBasicInformation (0x%8X)\n", status);
    }
    printf("PEB: %p\n", pbi.PebBaseAddress);

    // find the process parameters
    char *processParamsOffset = (char*)pbi.PebBaseAddress + 0x20; // hard coded offset for x64 apps
    char *processParameters = NULL;
    if(ReadProcessMemory(hProcess, processParamsOffset, &processParameters, sizeof(processParameters), NULL))
    {
        printf("UserProcessParameters: %p\n", processParameters);
    }
    else
    {
        printf("Error ReadProcessMemory (%u)\n", GetLastError());
    }

    // find the address to the environment table
    char *environmentOffset = processParameters + 0x80; // hard coded offset for x64 apps
    char *environment = NULL;
    ReadProcessMemory(hProcess, environmentOffset, &environment, sizeof(environment), NULL);
    printf("environment: %p\n", environment);

    // copy the environment table into our own memory for scanning
    wchar_t *localEnvBlock = new wchar_t[64*1024];
    ReadProcessMemory(hProcess, environment, localEnvBlock, sizeof(wchar_t)*64*1024, NULL);

    // find the variable to edit
    wchar_t *found = NULL;
    wchar_t *varOffset = localEnvBlock;
    while(varOffset < localEnvBlock + 64*1024)
    {
        if(varOffset[0] == '\0')
        {
            // we reached the end
            break;
        }
        if(wcsncmp(varOffset, L"ENVTEST=", 8) == 0)
        {
            found = varOffset;
            break;
        }
        varOffset += wcslen(varOffset)+1;
    }

    // check to see if we found one
    if(found)
    {
        size_t offset = (found - localEnvBlock) * sizeof(wchar_t);
        printf("Offset: %Iu\n", offset);

        // write a new version (if the size of the value changes then we have to rewrite the entire block)
        if(!WriteProcessMemory(hProcess, environment + offset, L"ENVTEST=def", 12*sizeof(wchar_t), NULL))
        {
            printf("Error WriteProcessMemory (%u)\n", GetLastError());
        }
    }

    // cleanup
    delete[] localEnvBlock;
    CloseHandle(hProcess);

    return 0;
}

样本输出:

>set ENVTEST=abc

>cppTest.exe 13796
Target PID: 13796
PEB: 000007FFFFFD3000
UserProcessParameters: 00000000004B2F30
environment: 000000000052E700
Offset: 1528

>set ENVTEST
ENVTEST=def

笔记

这种方法也将限于安全性限制。如果目标在更高的高度或更高的帐户(例如SYSTEM)下运行,则我们将无权编辑其内存。

如果要对32位应用程序执行此操作,则上面的硬编码偏移将分别更改为0x10和0x48。通过在调试器(例如WinDbg dt _PEBdt _RTL_USER_PROCESS_PARAMETERS)中转储_PEB和_RTL_USER_PROCESS_PARAMETERS结构,可以找到这些偏移量。

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

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


6

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

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

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

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

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

RefreshPath.cmd:

    @回声关闭

    REM此解决方案请求提升以便从注册表中读取。

    如果存在%temp%\ env.reg del%temp%\ env.reg / q / f

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

    如果不存在%temp%\ env.reg(
       回声“无法将注册表写入临时位置”
       1号出口
       )

    SETLOCAL EnableDelayedExpansion

    为/ f“令牌= 1,2 * delims ==” %% i in('type%temp%\ env.reg ^ | findstr -c:\“ Path \” =')做(
       设置== %%〜j
       回声!upath:\\ = \!>%temp%\ newpath
       )

     本地

     为/ f“ tokens = *”(%temp%\ newpath)中的%% i,请设置path = %% i

5
环境变量保留在注册表中。注册表中保留的是一个模板,Windows Explorer等程序在接到通知后会从中构造(重新)其环境变量。实际的环境变量是针对每个进程的,并存储在每个进程自己的地址空间中,最初是从其父进程继承而来的,此后可根据进程的异想天开进行修改。
JdeBP 2011年

5

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


5

重新启动资源管理器为我完成了此操作,但仅适用于新的cmd终端。

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

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

5

令人困惑的事情可能是有几个地方可以开始cmd。在我来说,我跑了从资源管理器窗口在cmd和环境变量并没有改变开始的时候,而从“运行” CMD(Windows键+ R)环境变量发生了变化

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

完成此操作后,我可以从Windows资源管理器中生成的cmd中访问新的环境变量。


3

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

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就足够了。
JasonXA

3

我喜欢匿名胆小鬼的回答中所说的巧克力味紧随其后的方法,因为它是纯批处理方法。但是,它留下一个临时文件和一些临时变量。我为自己做了一个清洁的版本。

refreshEnv.bat在您的计算机上的某个位置创建文件PATH。通过执行刷新控制台环境refreshEnv

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

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

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

ECHO Unknown command: %1
EXIT /b 1 

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

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

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

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

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

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

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

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

ECHO Environment successfully refreshed.

这也是%CLIENTNAME%吗?-没有为我工作- stackoverflow.com/questions/37550160/...
伊戈尔L.

%CLIENTNAME%在我的环境中不可用,通过阅读您的问题,我将假定它是由外部过程设置的。(当一个进程启动一个子进程时,它能够为该子进程调整环境。)由于它不是实际环境变量的一部分,因此不会被此脚本更新。
DieterDP '16

大家好@DieterDP,您的解决方案对我有用!我在64位计算机上使用Windows 10。我确实收到错误消息:“错误:系统无法找到指定的注册表项或值。”。但是,环境变量的更新是成功的。错误从何而来?
K.Mulier '16

如果不亲自测试就很难说,但是我猜想W10上的注册表结构可能会略有不同。如果您愿意,请尝试通过在命令行上执行命令来查找错误。
DieterDP '16

2

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

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

我有这个简单的批处理脚本,可以将Maven从Java7更改为Java8(都是环境变量)批处理文件夹位于PATH变量中,因此我始终可以在控制台和环境中的JAVA_HOME 变量中调用“ j8 ”被更改:

j8.bat:

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

到现在为止,我发现此方法最有效,最简单。您可能希望将其放在一个命令中,但是在Windows中根本不存在...


2

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

@echo off
rem Refresh PATH from registry.
setlocal
set USR_PATH=
set SYS_PATH=
for /F "tokens=3* skip=2" %%P in ('%SystemRoot%\system32\reg.exe query "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" /v PATH') do @set "SYS_PATH=%%P %%Q"
for /F "tokens=3* skip=2" %%P in ('%SystemRoot%\system32\reg.exe query "HKCU\Environment" /v PATH') do @set "USR_PATH=%%P %%Q"
if "%SYS_PATH:~-1%"==" " set "SYS_PATH=%SYS_PATH:~0,-1%"
if "%USR_PATH:~-1%"==" " set "USR_PATH=%USR_PATH:~0,-1%"
endlocal & call set "PATH=%SYS_PATH%;%USR_PATH%"
goto :EOF

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


我喜欢你的回答。发表了相同的答案我的问题在这里stackoverflow.com/q/61473551/1082063,我会接受它作为答案。谢谢。
David I. McIntosh

1

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

情况更糟:在Windows的旧版本中,您必须先注销然后再登录以考虑所做的更改...


1

我使用此Powershell脚本添加到PATH变量。我相信,只要稍加调整,它也可以在您的情况下工作。

#REQUIRES -Version 3.0

if (-not ("win32.nativemethods" -as [type])) {
    # import sendmessagetimeout from win32
    add-type -Namespace Win32 -Name NativeMethods -MemberDefinition @"
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern IntPtr SendMessageTimeout(
   IntPtr hWnd, uint Msg, UIntPtr wParam, string lParam,
   uint fuFlags, uint uTimeout, out UIntPtr lpdwResult);
"@
}

$HWND_BROADCAST = [intptr]0xffff;
$WM_SETTINGCHANGE = 0x1a;
$result = [uintptr]::zero

function global:ADD-PATH
{
    [Cmdletbinding()]
    param ( 
        [parameter(Mandatory=$True, ValueFromPipeline=$True, Position=0)] 
        [string] $Folder
    )

    # See if a folder variable has been supplied.
    if (!$Folder -or $Folder -eq "" -or $Folder -eq $null) { 
        throw 'No Folder Supplied. $ENV:PATH Unchanged'
    }

    # Get the current search path from the environment keys in the registry.
    $oldPath=$(Get-ItemProperty -Path 'Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment' -Name PATH).Path

    # See if the new Folder is already in the path.
    if ($oldPath | Select-String -SimpleMatch $Folder){ 
        return 'Folder already within $ENV:PATH' 
    }

    # Set the New Path and add the ; in front
    $newPath=$oldPath+';'+$Folder
    Set-ItemProperty -Path 'Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment' -Name PATH -Value $newPath -ErrorAction Stop

    # Show our results back to the world
    return 'This is the new PATH content: '+$newPath

    # notify all windows of environment block change
    [win32.nativemethods]::SendMessageTimeout($HWND_BROADCAST, $WM_SETTINGCHANGE, [uintptr]::Zero, "Environment", 2, 5000, [ref]$result)
}

function global:REMOVE-PATH {
    [Cmdletbinding()]
    param ( 
        [parameter(Mandatory=$True, ValueFromPipeline=$True, Position=0)]
        [String] $Folder
    )

    # See if a folder variable has been supplied.
    if (!$Folder -or $Folder -eq "" -or $Folder -eq $NULL) { 
        throw 'No Folder Supplied. $ENV:PATH Unchanged'
    }

    # add a leading ";" if missing
    if ($Folder[0] -ne ";") {
        $Folder = ";" + $Folder;
    }

    # Get the Current Search Path from the environment keys in the registry
    $newPath=$(Get-ItemProperty -Path 'Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment' -Name PATH).Path

    # Find the value to remove, replace it with $NULL. If it's not found, nothing will change and you get a message.
    if ($newPath -match [regex]::Escape($Folder)) { 
        $newPath=$newPath -replace [regex]::Escape($Folder),$NULL 
    } else { 
        return "The folder you mentioned does not exist in the PATH environment" 
    }

    # Update the Environment Path
    Set-ItemProperty -Path 'Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment' -Name PATH -Value $newPath -ErrorAction Stop

    # Show what we just did
    return 'This is the new PATH content: '+$newPath

    # notify all windows of environment block change
    [win32.nativemethods]::SendMessageTimeout($HWND_BROADCAST, $WM_SETTINGCHANGE, [uintptr]::Zero, "Environment", 2, 5000, [ref]$result)
}


# Use ADD-PATH or REMOVE-PATH accordingly.

#Anything to Add?

#Anything to Remove?

REMOVE-PATH "%_installpath_bin%"

1

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

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

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

[文件:task.cmd]

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

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

[文件:refresh.ps1]

function Update-Environment {
    $locations = 'HKLM:\SYSTEM\CurrentControlSet\Control\Session  Manager\Environment',
                 'HKCU:\Environment'
    $locations | ForEach-Object {
        $k = Get-Item $_
        $k.GetValueNames() | ForEach-Object {
            $name  = $_
            $value = $k.GetValue($_)

            if ($userLocation -and $name -ieq 'PATH') {
                $env:Path += ";$value"
            } else {

                Set-Item -Path Env:\$name -Value $value
            }
        }
        $userLocation = $true
    }
}
Update-Environment
#Here we can use newly added environment variables like for example npm install.. 
npm install -g create-react-app serve

Powershell脚本结束后,cmd脚本将继续执行其他任务。现在,要记住的一件事是,在任务完成之后,即使powershell脚本已在其自己的会话中更新了这些环境变量,cmd仍然无法访问新的环境变量。这就是为什么我们在powershell脚本中执行所有必需的任务的原因,这些脚本可以调用与cmd当然相同的命令。


0

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

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

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

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

-1

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

我的批处理脚本可以做到这一点:

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

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

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.