Windows의 명령 프롬프트에서 환경 변수를 새로 고치는 명령이 있습니까?


480

환경 변수를 수정하거나 추가하면 명령 프롬프트를 다시 시작해야합니다. CMD를 다시 시작하지 않고 실행할 수있는 명령이 있습니까?


38
실제로이를 확인해야하는 모든 프로그램을 다시 시작해야합니다. 환경은 시작시 프로세스의 메모리에 복사되므로 더 이상 시스템 정의 envvar에 연결되지 않습니다.
Joey

15
이것들을 읽은 후에, 나는 현실 세계에 숟가락이 없다는 것을 깨달았습니다 .) cmd를 다시 시작하면됩니다.
n611x007

명령이 아니므로 정답은 아니지만 다음을 올바르게 읽으면 Win32 API를 사용하여 지원됩니다. support.microsoft.com/en-us/help/104011/… 해당 행을 간단한 C 프로그램을 실행하고 환경 변수 업데이트에 따라 실행하십시오.
Charles Grunwald 5

이 스레드에 따라 cmd.exe 창에서 WM_SETTINGCHANGE (@CharlesGrunwald가 언급 한 win32 API)가 작동하지 않습니다. github.com/chocolatey/choco/issues/1589- 이것이 refreshenv 명령을 쓴 이유
davr

답변:


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 스크립트에서 환경 변수를 명령 프롬프트로 다시 내보내는 간단한 방법을 찾지 못했습니다.

비. PATH 환경 변수는 사용자와 시스템 PATH 변수를 연결 한 것입니다.

사용자와 시스템 간의 변수 충돌에 대한 일반적인 규칙이 무엇인지 확실하지 않으므로 구체적으로 처리되는 PATH 변수를 제외하고 사용자가 시스템을 재정의하도록 선택했습니다.

이상한 vbs + 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"

2
FOR / F "tokens = 1, *"%% c IN ( 'resetvars.vbs') DO 구문을 사용하여 임시 파일을 피할 수 있습니다.
tzot

2
내 대답에서 말했듯이 "또는 기존 명령 프롬프트에서 SET을 사용하여 수동으로 추가하십시오." 이것이 효과적으로하는 것입니다. 그래도 좋은 대답입니다.
Kev

2
@itsadok-이것이 이제는 정답이라는 것을 감안할 때, 처음에 간단한 설명을 추가하여 스크립트를 맥락에 넣으십시오. 즉, 위와 같이 수동으로 업데이트하거나 cmd.exe를 다시 시작하지 않고 env var 변경 사항을 열린 cmd.exe로 전파 할 수는 없습니다.
Kev

이 스크립트는 "내 컴퓨터 ... 환경 변수"에서 환경 변수를 전역 적으로 변경하는 유스 케이스를 처리하지만, 하나의 cmd.exe에서 환경 변수가 변경되면 스크립트는이를 실행중인 다른 cmd.exe로 전파하지 않습니다. 아마도 일반적인 시나리오 일 것입니다.
Kev

1
@Keyslinger : 실제로는 불가능합니다. 생성 된 모든 프로그램은 자체 환경을 업데이트 할 수 있지만 실행중인 cmd.exe 인스턴스의 환경은 업데이트 할 수 없습니다. 배치 파일은 cmd.exe의 동일한 인스턴스 내에서 실행되기 때문에 cmd.exe 환경을 업데이트 할 수 있습니다.
Ben Voigt

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현재 세션에 업데이트 된 환경 변수를 가져 오기 위해 실행할 수 있습니다 .
Martin Valgur

2
이것은 매우 유용한 유틸리티 소프트웨어입니다. 공유해 주셔서 감사합니다.
Sabuncu

10
참고 : Chocolatey가 저장소를 옮기고이
Michael Burr

1
이것도 작동해야 Powershell합니까? 그것은 cmd.exe나를 위해 일하는 것 같습니다 .
craq

1
PowerShell @craq에서 작동합니다. Windows10 x64 실행
mazunki 2016 년

100

Windows 7/8/10에서는이 기본 제공 스크립트가있는 Chocolatey를 설치할 수 있습니다.

Chocolatey를 설치 한 후을 입력하십시오 refreshenv.


이것은 유효한 답변입니다. downvoted 한 사람의 이야기를 들으면 좋을 것입니다.
the_joric

2
내가 무엇을 잘못하고 있지? $> refreshenv 'refreshenv'는 내부 또는 외부 명령, 실행 가능한 프로그램 또는 배치 파일로 인식되지 않습니다.
aclokay

@aclokay 확실하지 않습니다. 디버깅 할 ur 시스템 conf에 대한 자세한 내용을 제공하십시오. 한편 여기에서 비슷한 공개 문제를 참조 할 수 있습니다. github.com/chocolatey/choco/issues/250
jolly

그것은 나에게도 효과가 없다. 나는 W7 전문가입니다. 아마도 더 완전한 버전에서만 작동합니다.
밖의

1
refreshenv가 스크립트를 너무 일찍 존재하는 경우 (나와 마찬가지로) "call RefreshEnv.cmd"를 대신 사용할 수 있습니다. ( github.com/chocolatey/choco/issues/1461 참조 )
sfiss

59

디자인에 의해 내장되어 있지 않습니다 으로 다른 cmd.exe 또는 "내 컴퓨터-> 속성-> 고급 설정->에서 이미 실행중인 cmd.exe에 환경 변수 추가 / 변경 / 제거를 전달하는 Windows 용 메커니즘 환경 변수".

기존의 열린 명령 프롬프트 범위 밖에서 새 환경 변수를 수정하거나 추가하는 경우 명령 프롬프트를 다시 시작하거나 기존 명령 프롬프트에서 SET을 사용하여 수동으로 추가해야합니다.

가장 최근에 승인 된 답변 은 스크립트에서 모든 환경 변수를 수동으로 새로 고침하여 부분적인 해결 방법을 보여줍니다 . 이 스크립트는 "내 컴퓨터 ... 환경 변수"에서 환경 변수를 전역으로 변경하는 유스 케이스를 처리하지만 한 cmd.exe에서 환경 변수가 변경되면 스크립트는이를 실행중인 다른 cmd.exe로 전파하지 않습니다.


누군가이 문제에 대한 해결책을 가지고 있다면 주기적으로 체크인하고 수락 된 답변을 변경할 수 있습니다.
Eric Schoonover

1
이것은 단순히 질문에 대답하지 않기 때문에 허용되는 대답이 아니어야합니다. 이 질문은 답을 찾을 때까지 허용 된 대답없이 남아 있어야합니다.
shoosh

4
그리고 귀찮게도 cmd.exe의 추가 인스턴스는 계산되지 않습니다. 그들은 모두 변경이 새로운 cmd.exe를의에 반영되기 전에 사망 할 수 있습니다.

6
이 답변의 부정적인 의견과 아래로 표시는 때때로 스택 오버플로가 얼마나 깨진 지 보여줍니다. 케빈이 정답을 주었다. 당신이 그것을 좋아하지 않기 때문에 그것을 표시 할 이유가 없습니다.
David Arno

Kev는 분명히 그 질문에 대답합니다. 문제는 내장 솔루션이 없다는 것입니다.
Eric Schoonover

40

나는 더 쉬운 해결책을 찾기 전에이 대답을 보았습니다.

explorer.exe작업 관리자에서 다시 시작하면 됩니다.

테스트하지는 않았지만 명령 프롬프트를 다시 열어야 할 수도 있습니다.

Timo Huovinen의 크레디트 : 노드가 성공적으로 설치되었지만 인식되지 않습니다 .


나는이 블로그에 설명 된대로 내 솔루션의 루트에서 명령 프롬프트를 열 수 있도록 Visual Studio에 외부 도구를 추가하려고했기 때문에 여기에 도착했습니다 : neverindoubtnet.blogspot.com/2012/10/… ... 비슷한 문제가 발생했습니다 ... 경로 변수에 "git"을 표시하려고했습니다. git 디렉토리를 PATH 변수에 추가했지만 Visual Studio에서 열라는 명령 프롬프트에 표시되지 않습니다. 간단한 해결책은 Visual Studio를 다시 시작하는 것이 었습니다. 그런 다음 PATH 변수에 새로 추가 된 내용이 cmd에 표시되었습니다.
David Barrows 2013

4
그 솔루션은 10 Windows에서 저를하는 데 도움이
ganchito55

7
문제는 " CMD를 다시 시작하지 않고 실행할 수있는 명령이 있습니까?" 였습니다.
Florian F

작업 관리자에서 explorer.exe를 다시 시작할 수 없었습니다. 나는 그것을했지만 내 작업 표시 줄이 깨졌습니다. explorer; exe를 시작하는 것은 정말 간단합니다. "Ctrl + shift + escape"-> 파일-> "새 작업 실행"-> "explorer.exe"가 나를 위해 일을하도록하겠습니다. 그리고 네, 모든 env var가 새로운 cmd 창에서 사용되었습니다. 모두 감사합니다
Oscar

좋은 해결책, 감사합니다! @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). screenvideo
Igor

26
이렇게해도 질문이 해결되지도 않습니다. 원래 질문은 환경 변수를 해당 터미널에서 설정 한 값으로 새로 고치는 방법입니다.
csauve 2016 년

이 질문에 대한 답변은 아니지만 최상의 솔루션의 절반을 제공합니다. 설정하는 모든 변수에 대해 이것을 사용하고 제어판을 열고 환경 변수를 전역 적으로 추가합니다. 나는 setx그것이 영구적으로 원하는 것이 아니라 수정 된 변수를 가질 수있는 현재 환경을 상속하기 때문에 사용하는 것을 좋아하지 않습니다 . 이 방법을 사용하면 변수를 사용하기 위해 콘솔을 다시 시작하지 않아도되며 향후 전역에서 사용할 수없는 문제를 피할 수 있습니다.
dgo

25

"setx"를 사용하고 cmd 프롬프트를 재시작하십시오

이 작업 에는 " setx " 라는 명령 행 도구가 있습니다 . env 변수 를 읽고 쓰는 데 사용 됩니다. 변수는 명령 창이 닫힌 후에도 유지됩니다.

"프로그래밍이나 스크립팅없이 사용자 또는 시스템 환경에서 환경 변수를 작성하거나 수정합니다. setx 명령은 레지스트리 키의 값을 검색하여 텍스트 파일에 씁니다."

참고 :이 도구로 만들거나 수정 한 변수는 향후 명령 창에서 사용할 수 있지만 현재 CMD.exe 명령 창에서는 사용할 수 없습니다. 따라서 다시 시작해야합니다.

setx누락 된 경우 :


또는 레지스트리를 수정하십시오.

MSDN의 말 :

시스템 환경 변수를 프로그래밍 방식으로 추가하거나 수정하려면 HKEY_LOCAL_MACHINE \ System \ CurrentControlSet \ Control \ Session Manager \ Environment 레지스트리 키에 변수를 추가 한 다음 lParam 을 문자열 " Environment "로 설정하여 WM_SETTINGCHANGE 메시지 를 브로드 캐스트하십시오 .

이를 통해 셸과 같은 응용 프로그램에서 업데이트를 선택할 수 있습니다.


1
setx를 사용하여 환경 변수를 읽는 방법을 확장 할 수 있습니까? 나는 다양한 문서를 다뤘으 며 그것을 보지 못했습니다. :-/
Mark Ribau

2
setx VARIABLE -k "HKEY_LOCAL_MACHINE \ Software \ Microsoft \ WindowsNT \ CurrentVersion \ CurrentVersion"echo % VARIABLE %
Jens A. Koch

3
현재 시스템 환경 : HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment\VARIABLE현재 사용자 환경 : HKEY_CURRENT_USER\Environment\VARIABLE
Mark Ribau

5
setx /? "로컬 시스템에서이 도구로 작성하거나 수정 한 변수는 향후 명령 창에서는 사용할 수 있지만 현재 CMD.exe 명령 창 에서는 사용할 수 없습니다 ." OP는 현재 cmd를 업데이트하려고했습니다.
Superole

4
구매자는 조심하십시오! 당신이있는 경우 특히 긴 %PATH%setx1024 바이트이를자를 수 있습니다! 그와 마찬가지로 그의 저녁은 사라졌습니다
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에서는 탐색기가 단일 인스턴스로만 실행되며 일반적으로 로그인 한 사람 만 실행됩니다. 로컬 시스템에서 실행하거나 상자에서 관리자로 실행하는 것과 상관없이 작업을 수행하십시오. 이것의 한계는 경로와 같은 것을 처리하지 않는다는 것입니다. 이것은 단순한 환경 변수에서만 작동했습니다. 이것은 배치를 사용하여 (공백이있는) 디렉토리로 가져 와서 .exe 등을 실행하는 파일로 복사 할 수있게했습니다.

원래 배치는 새 배치를 호출합니다.

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

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로 변경됩니다. 이 오프셋은 디버거에서 _PEB 및 _RTL_USER_PROCESS_PARAMETERS 구조체를 덤프하여 찾을 수 있습니다 (예 : WinDbg dt _PEBdt _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\"

따라서 시스템 속성의 내용으로 현재 명령 세션에서 경로 변수를 새로 고치려면 다음 배치 스크립트가 올바르게 작동합니다.

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 \ 환경"

    존재하지 않는 경우 % temp % \ env.reg (
       echo "임시 위치에 레지스트리를 쓸 수 없습니다"
       1 번 출구
       )

    SETLOCAL EnableDelayedExpansion

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

     내향

     / f "tokens = *"에 대해 (% temp % \ newpath)의 %% i는 path = %% i를 설정합니다.

5
환경 변수는 레지스트리에 유지 되지 않습니다 . 레지스트리에 보관되는 것은 Windows 탐색기와 같은 프로그램에서 알림을받을 때 환경 변수를 재구성 하는 템플릿 입니다. 실제 환경 변수는 프로세스별로 구성되며 각 프로세스의 고유 한 주소 공간에 저장되며, 초기에는 상위 프로세스에서 상속되고 그 이후 프로세스에서 수정 될 수 있습니다.
JdeBP

5

관리자 권한으로 새 명령 프롬프트를 열어보십시오. 이것은 Windows 10에서 나를 위해 일했습니다. (이것은 오래된 대답이라는 것을 알고 있지만 VBS 스크립트를 작성해야하기 때문에 이것을 공유해야합니다.)


5

탐색기를 다시 시작하면 나를 위해이 작업을 수행했지만 새로운 cmd 터미널에서만이 작업을 수행했습니다.

경로를 설정 한 터미널은 이미 새 경로 변수를 볼 수있었습니다 (Windows 7).

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

5

혼란스러운 것은 cmd를 시작할 곳이 몇 군데있을 수 있습니다. 내 경우에는 내가 도망 탐색기 창에서 cmd를 하고 환경 변경하지 않은 변수를 시작할 때 상태 "실행"에서 cmd를 환경 (윈도우 키 + 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%

사용하여 SET를 한 후 SETX 는 명령 창을 다시 시작하지 않고 직접 "로컬"변수를 사용하는 것이 가능하다. 그리고 다음 번에는 환경 변수가 사용됩니다.


내가 한 일을 얻는 동안, 그는 병렬 스크립트를 위해 무언가를 원할 것입니다. 하나의 스크립트는 전역을 설정하고 다른 하나는 전역 스크립트를 읽습니다. 그렇지 않으면, 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

@DieterDP 안녕하세요, 귀하의 솔루션이 저에게 효과적입니다! 64 비트 컴퓨터에서 Windows 10을 사용하고 있습니다. "오류 : 시스템이 지정된 레지스트리 키 또는 값을 찾을 수 없습니다."라는 오류가 발생합니다. 그럼에도 불구하고 환경 변수가 성공적으로 업데이트되었습니다. 오류는 어디에서 발생합니까?
K.Mulier

실제로 직접 테스트하지 않고 말하기는 어렵지만 W10의 레지스트리 구조는 약간 다를 수 있습니다. 느낌이들 경우 명령 줄에서 명령을 실행하여 오류를 찾아보십시오.
DieterDP

2

변경하려는 특정 변수 중 하나 (또는 ​​몇 가지)에만 관심이 있다면 가장 쉬운 방법은 해결 방법이라고 생각 합니다. 환경과 현재 콘솔 세션에서 설정하십시오.

  • 세트는 현재 세션에 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에는 단순히 존재하지 않습니다 ...


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

케프가 말한 것처럼 직접적인 방법은 없습니다. 대부분의 경우 다른 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

Windows에서도 환경 변수를 갱신하면 많은 자동화 작업을 수행 할 수 있기 때문에 2019 년에도 매우 흥미로운이 질문을 게시 해 주셔서 감사합니다 (실제로 위에서 언급 한 단일 인스턴스이므로 쉘 cmd를 갱신하는 것은 쉽지 않습니다) 명령 줄을 수동으로 다시 시작해야합니다.

예를 들어,이 소프트웨어를 사용하여 정기적으로 재설치하는 많은 컴퓨터에 소프트웨어를 배포하고 구성 할 수 있습니다. 또한 소프트웨어를 배포하는 동안 명령 줄을 다시 시작해야하는 것은 매우 비현실적이며 반드시 유쾌하지 않은 해결 방법을 찾아야한다는 점을 인정해야합니다. 우리 문제에 갑시다. 다음과 같이 진행합니다.

1-우리는 다음과 같은 powershell 스크립트를 호출하는 배치 스크립트를 가지고 있습니다

[파일 : task.cmd] .

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

이 문제를 해결하기 위해 BOTH setx 및 set을 사용하여 환경 변수를 변경 한 다음 explorer.exe의 모든 인스턴스를 다시 시작했습니다. 이 방법으로 이후에 시작된 프로세스는 새로운 환경 변수를 갖습니다.

이 작업을 수행하는 배치 스크립트 :

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

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

이 방법의 문제점은 현재 열려있는 모든 탐색기 창이 닫히는 것입니다. 아마도 나쁜 생각입니다. 그러나 Kev의 게시물을 참조하여 이것이 필요한 이유를 알아보십시오

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.