如果我修改或添加环境变量,我必须重新启动命令提示符.是否有一个我可以执行的命令可以在不重新启动CMD的情况下执行此操作?
您可以使用vbs脚本捕获系统环境变量,但是您需要一个bat脚本来实际更改当前环境变量,因此这是一个组合解决方案.
创建一个resetvars.vbs
包含此代码的文件,并将其保存在路径中:
Set oShell = WScript.CreateObject("WScript.Shell")
filename = oShell.ExpandEnvironmentStrings("%TEMP%\resetvars.bat")
Set objFileSystem = CreateObject("Scripting.fileSystemObject")
Set oFile = objFileSystem.CreateTextFile(filename, TRUE)
set oEnv=oShell.Environment("System")
for each sitem in oEnv
oFile.WriteLine("SET " & sitem)
next
path = oEnv("PATH")
set oEnv=oShell.Environment("User")
for each sitem in oEnv
oFile.WriteLine("SET " & sitem)
next
path = path & ";" & oEnv("PATH")
oFile.WriteLine("SET PATH=" & path)
oFile.Close
创建另一个包含此代码的文件名resetvars.bat,位置相同:
@echo off
%~dp0resetvars.vbs
call "%TEMP%\resetvars.bat"
如果要刷新环境变量,只需运行即可 resetvars.bat
护教学:
我提出这个解决方案的两个主要问题是
一个.我找不到一种直接的方法将环境变量从vbs脚本导出回命令提示符,并且
湾 PATH环境变量是用户和系统PATH变量的串联.
我不确定用户和系统之间冲突变量的一般规则是什么,所以我选择了用户覆盖系统,除了专门处理的PATH变量.
我使用奇怪的vbs + bat +临时bat机制来解决从vbs导出变量的问题.
注意:此脚本不会删除变量.
这可能会有所改善.
添加
如果需要将环境从一个cmd窗口导出到另一个cmd窗口,请使用此脚本(让我们称之为exportvars.vbs
):
Set oShell = WScript.CreateObject("WScript.Shell")
filename = oShell.ExpandEnvironmentStrings("%TEMP%\resetvars.bat")
Set objFileSystem = CreateObject("Scripting.fileSystemObject")
Set oFile = objFileSystem.CreateTextFile(filename, TRUE)
set oEnv=oShell.Environment("Process")
for each sitem in oEnv
oFile.WriteLine("SET " & sitem)
next
oFile.Close
运行exportvars.vbs
中要导出的窗口从,然后切换到要导出的窗口来,并键入:
"%TEMP%\resetvars.bat"
这就是Chocolatey使用的.
https://github.com/chocolatey/choco/blob/master/src/chocolatey.resources/redirects/RefreshEnv.cmd
@echo off :: :: RefreshEnv.cmd :: :: Batch file to read environment variables from registry and :: set session variables to these values. :: :: With this batch file, there should be no need to reload command :: environment every time you want environment changes to propagate echo | set /p dummy="Reading environment variables from registry. Please wait... " goto main :: Set one environment variable from registry key :SetFromReg "%WinDir%\System32\Reg" QUERY "%~1" /v "%~2" > "%TEMP%\_envset.tmp" 2>NUL for /f "usebackq skip=2 tokens=2,*" %%A IN ("%TEMP%\_envset.tmp") do ( echo/set %~3=%%B ) goto :EOF :: Get a list of environment variables from registry :GetRegEnv "%WinDir%\System32\Reg" QUERY "%~1" > "%TEMP%\_envget.tmp" for /f "usebackq skip=2" %%A IN ("%TEMP%\_envget.tmp") do ( if /I not "%%~A"=="Path" ( call :SetFromReg "%~1" "%%~A" "%%~A" ) ) goto :EOF :main echo/@echo off >"%TEMP%\_env.cmd" :: Slowly generating final file call :GetRegEnv "HKLM\System\CurrentControlSet\Control\Session Manager\Environment" >> "%TEMP%\_env.cmd" call :GetRegEnv "HKCU\Environment">>"%TEMP%\_env.cmd" >> "%TEMP%\_env.cmd" :: Special handling for PATH - mix both User and System call :SetFromReg "HKLM\System\CurrentControlSet\Control\Session Manager\Environment" Path Path_HKLM >> "%TEMP%\_env.cmd" call :SetFromReg "HKCU\Environment" Path Path_HKCU >> "%TEMP%\_env.cmd" :: Caution: do not insert space-chars before >> redirection sign echo/set Path=%%Path_HKLM%%;%%Path_HKCU%% >> "%TEMP%\_env.cmd" :: Cleanup del /f /q "%TEMP%\_envset.tmp" 2>nul del /f /q "%TEMP%\_envget.tmp" 2>nul :: Set these variables call "%TEMP%\_env.cmd" echo | set /p dummy="Done" echo .
在Windows 7/8/10上,你可以安装Chocolatey,它有一个内置的脚本.
安装Chocolatey后,只需输入不带引号的"refreshenv".
根据设计,Windows 没有内置机制将环境变量add/change/remove传播到已经运行的cmd.exe,可以是另一个cmd.exe,也可以是"我的电脑 - >属性 - >高级设置 - >环境变量".
如果在现有打开命令提示符的范围之外修改或添加新环境变量,则需要重新启动命令提示符,或者在现有命令提示符中使用SET手动添加.
在最新接受的答案显示了手动刷新局部变通的所有环境变量的脚本.该脚本在"我的电脑...环境变量"中处理全局更改环境变量的用例,但如果在一个cmd.exe中更改了环境变量,则脚本不会将其传播到另一个正在运行的cmd.exe.
在找到更简单的解决方案之前,我遇到了这个答案.
只需explorer.exe
在任务管理器中重启
我没有测试,但您可能还需要重新打开命令提示符.
感谢蒂莫Huovinen这里:节点无法识别,虽然安装成功(如果这帮助了你,请去给这人的评论信用).
这适用于Windows 7: SET PATH=%PATH%;C:\CmdShortcuts
通过键入echo%PATH%进行测试,它运行良好.还设置如果你打开一个新的cmd,不再需要那些讨厌的重新启动:)
使用"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)获取更新.
调用此函数对我有用:
VOID Win32ForceSettingsChange() { DWORD dwReturnValue; ::SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, (LPARAM) "Environment", SMTO_ABORTIFHUNG, 5000, &dwReturnValue); }
我想出的最好的方法是只进行注册表查询.这是我的例子.
在我的示例中,我使用添加了新环境变量的Batch文件进行了安装.我需要在安装完成后立即执行此操作,但无法使用这些新变量生成新进程.我测试了产生另一个浏览器窗口并回调cmd.exe并且这有效但是在Vista和Windows 7上,资源管理器仅作为单个实例运行,并且通常作为登录的人运行.这将自动失败,因为我需要我的管理员信誉无论是从本地系统运行还是作为管理员运行,都可以执行操作.对此的限制是它不处理像path这样的东西,这只适用于简单的环境变量.这允许我使用批处理来到目录(带空格)并复制文件运行.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
还有另一种方法,我从各种不同的想法中提出.请看下面.这基本上会从注册表获取最新的路径变量但是,这将导致一些问题,因为注册表查询本身会给出变量,这意味着每个地方都有一个变量,这将无法工作,所以要解决这个问题我基本上加倍了路径.非常讨厌.更多的方法是:Set Path =%Path%; C:\ Program Files\Software .... \
无论这里是新的批处理文件,请谨慎使用.
@ECHO OFF SETLOCAL ENABLEEXTENSIONS set org=%PATH% for /f "tokens=2*" %%A in ('REG QUERY "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" /v Path ^|FIND /I "Path"') DO ( SET path=%%B ) SET PATH=%org%;%PATH% set path
可以通过在指定进程本身内覆盖环境表来完成此操作.
作为概念验证,我编写了这个示例应用程序,它只是在cmd.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中例如在调试器倾出_PEB和_RTL_USER_PROCESS_PARAMETERS结构中找到dt _PEB
和dt _RTL_USER_PROCESS_PARAMETERS
)
要将概念验证更改为OP所需的内容,它只需枚举当前系统和用户环境变量(例如@ tsadok的答案记录),并将整个环境表写入目标进程的内存中.
编辑:环境块的大小也存储在_RTL_USER_PROCESS_PARAMETERS结构中,但内存是在进程堆上分配的.因此,从外部流程我们无法调整大小并使其变大.我一直在使用VirtualAllocEx在目标进程中为环境存储分配额外的内存,并且能够设置和读取一个全新的表.不幸的是,任何尝试从正常方式修改环境都会崩溃并烧毁,因为地址不再指向堆(它将在RtlSizeHeap中崩溃).
环境变量保存在HKEY_LOCAL_MACHINE\SYSTEM\ControlSet\Control\Session Manager\Environment中.
许多有用的env变量(如Path)都存储为REG_SZ.有几种方法可以访问注册表,包括REGEDIT:
REGEDIT /E <filename> "HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Session Manager\Environment"
输出以幻数开头.因此,要使用find命令搜索它,需要对其进行输入和重定向:type
因此,如果您只想使用系统属性中的内容刷新当前命令会话中的路径变量,则以下批处理脚本可以正常工作:
RefreshPath.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 10上对我有用。(我知道这是个老答案,但是我不得不分享这个,因为仅为此写一个VBS脚本是荒谬的)。
在当前会话中不重新启动变量的情况下,将变量添加到路径的最简单方法是打开命令提示符并键入:
PATH=(VARIABLE);%path%
然后按enter。
检查您的变量是否已加载,键入
PATH
然后按enter。但是,在重新启动之前,变量将仅是路径的一部分。
令人困惑的事情可能是有几个地方可以开始cmd。在我来说,我跑了从资源管理器窗口在cmd和环境变量并没有改变开始的时候,而从“运行” CMD(Windows键+ R)环境变量发生了变化。
就我而言,我只需要从任务管理器中终止Windows资源管理器进程,然后再从任务管理器中重新启动它即可。
完成此操作后,我可以从Windows资源管理器中生成的cmd中访问新的环境变量。