Windows'taki komut isteminden ortam değişkenlerini yenilemek için bir komut var mı?


480

Bir ortam değişkenini değiştirir veya eklersem, komut istemini yeniden başlatmam gerekir. CMD'yi yeniden başlatmadan bunu yapabileceğim bir komut var mı?


38
Aslında, onları görmesi gereken her program yeniden başlatılmalıdır. Ortam, başlangıçta işlemin belleğine kopyalanır ve bu nedenle artık sistem tanımlı ortamlarla hiçbir bağlantısı yoktur.
Joey

15
bunları okuduktan sonra kaşık olmadığını fark ettim ;) gerçek dünyada sadece cmd'yi yeniden başlatırsınız.
n611x007

Bir komut değil, bu yüzden bir cevap değil, ancak aşağıdakileri doğru okursam Win32 API kullanarak destek var: support.microsoft.com/en-us/help/104011/… Bu satırı bir basit C programı ve ortam değişkeni güncellemeleri takip çalıştırın.
Charles Grunwald

WM_SETTINGCHANGE (@CharlesGrunwald tarafından belirtilen win32 api) bu konuya göre cmd.exe pencereleri için çalışmaz: github.com/chocolatey/choco/issues/1589 - refreshenv komutunu yazmasının nedeni bu
davr

Yanıtlar:


137

Sistem ortamı değişkenlerini bir vbs komut dosyasıyla yakalayabilirsiniz, ancak geçerli ortam değişkenlerini gerçekten değiştirmek için bir yarasa komut dosyasına ihtiyacınız vardır, bu nedenle bu birleşik bir çözümdür.

resetvars.vbsBu kodu içeren bir dosya oluşturun ve yola kaydedin:

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

bu kodu içeren başka bir dosya adı sıfırla .bat, aynı konumda:

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

Ortam değişkenlerini yenilemek istediğinizde, resetvars.bat


Özür dileme :

Bu çözümü ortaya koyduğum iki temel sorun

a. Ortam değişkenlerini bir vbs komut dosyasından komut istemine geri vermenin basit bir yolunu bulamadım ve

b. PATH ortam değişkeni, kullanıcının ve sistem PATH değişkenlerinin bir birleşimidir.

Kullanıcı ve sistem arasındaki değişkenler için genel kural ne olduğundan emin değilim, bu yüzden özellikle işlenen PATH değişkeni dışında, kullanıcı geçersiz kılma sistemi yapmak için seçildi.

Değişkenleri vbs'den dışa aktarma sorununu çözmek için garip vbs + bat + geçici bat mekanizmasını kullanıyorum.

Not : bu komut dosyası değişkenleri silmez.

Bu muhtemelen geliştirilebilir.

KATMA

Ortamı bir cmd penceresinden diğerine dışa aktarmanız gerekiyorsa, bu komut dosyasını kullanın (diyelim ki 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

Run exportvars.vbsEğer ihracat istediğiniz pencerede gelen , o zaman ihracat istediğiniz pencereye geçiş için ve tipi:

"%TEMP%\resetvars.bat"

2
Belki de FOR / F "belirteçleri = 1, *" %% c IN ('resetvars.vbs')
kullanarak YAPILABİLİRSİNİZ

2
Cevabımda söylediğim gibi "veya mevcut komut isteminde SET kullanarak el ile ekleyin." bu etkili bir şekilde yapıyor. İyi cevap olsa.
Kev

2
@itsadok - bunun artık kabul edilen cevap olduğu göz önüne alındığında, senaryoyu bağlama koymak için başlangıçta kısa bir açıklama eklemelisiniz. yani yukarıdaki gibi manuel olarak güncelleme yapmadan veya cmd.exe'yi yeniden başlatarak bir env var değişikliğini açık bir cmd.exe'ye yaymanın mümkün olmadığını belirtin.
Kev

Komut dosyası, "Bilgisayarım ... Ortam Değişkenleri" nde ortam değişkenlerini genel olarak değiştirmenin kullanım durumunu işler, ancak bir cmd.exe'de ortam değişkeni değiştirilirse, komut dosyası bunu düşündüğüm başka bir çalışan cmd.exe'ye yaymaz muhtemelen ortak bir senaryo.
Kev

1
@Keyslinger: Bu aslında mümkün değil. Ortaya çıkan herhangi bir program kendi ortamını güncelleyebilir, ancak çalışan cmd.exe örneğinin sürümünü güncelleştiremez. Bir toplu iş dosyası cmd.exe ortamını güncelleyebilir, çünkü cmd.exe dosyasının aynı örneğinde çalışır.
Ben Voigt

112

İşte Chocolatey ne kullanıyor.

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 yüklüyse, RefreshEnvgüncel oturumunuza güncellenmiş ortam değişkenlerini almak için çalışabilirsiniz .
Martin Valgur

2
Bu inanılmaz faydalı bir yardımcı programdır, paylaştığınız için çok teşekkürler.
Sabuncu

10
Not: Chocolatey depoları taşıdı ve bu komut dosyasının en son sürümünü (bazı hata düzeltmeleriyle) burada bulabilirsiniz: github.com/chocolatey/choco/blob/master/src/…
Michael Burr

1
bu da işe Powershellyaramalı mı? Sadece cmd.exebenim için çalışıyor gibi görünüyor .
craq

1
Benim için PowerShell'de çalışıyor, @craq. Windows10 x64 çalıştırılıyor.
mazunki

100

Windows 7/8/10'da, bu yerleşik için bir komut dosyası olan Chocolatey'yi yükleyebilirsiniz.

Chocolatey'i kurduktan sonra yazın refreshenv.


Bu geçerli bir cevap, onu aşağı indirdi adamdan duymak güzel olurdu
the_joric

2
Neyi yanlış yapıyorum? $> refreshenv 'refreshenv' dahili veya harici bir komut, çalıştırılabilir program veya toplu iş dosyası olarak tanınmıyor.
aclokay

@aclokay Emin değilim. Hata ayıklamak ur sistemi conf hakkında daha fazla bilgi veriniz. Bu arada burada benzer bir açık konuya başvurabilirsiniz. github.com/chocolatey/choco/issues/250
neşeli

Benim için de işe yaramıyor. W7 profesyonelindeyim, belki sadece daha eksiksiz sürümlerde çalışır.
alseether

1
Refreshenv betiğinizi zamanından önce (benim için olduğu gibi) varsa, bunun yerine "call RefreshEnv.cmd" kullanabilirsiniz. (bkz. github.com/chocolatey/choco/issues/1461 )
sfiss

59

Tasarım gereği bir değil orada yerleşik Windows için mekanizmanın başka cmd.exe veya My Computer" dan, ya zaten çalışan cmd.exeye bir ortam değişkeni ekleme / değiştirme / kaldırmayı yaymak için -> Özellikler -> Gelişmiş Ayarlar -> Ortam Değişkenleri".

Varolan bir açık komut isteminin kapsamı dışında yeni bir ortam değişkeni değiştirir veya eklerseniz, komut istemini yeniden başlatmanız veya varolan komut isteminde SET'i kullanarak el ile eklemeniz gerekir.

Kabul edilen en son yanıt , bir komut dosyasındaki tüm ortam değişkenlerini el ile yenileyerek kısmi bir çözüm olduğunu gösterir . Komut dosyası, "Bilgisayarım ... Ortam Değişkenleri" nde ortam değişkenlerini genel olarak değiştirmenin kullanım örneğini işler, ancak bir cmd.exe dosyasında bir ortam değişkeni değiştirilirse, komut dosyası bunu çalışan başka bir cmd.exe dosyasına yaymaz.


Herhangi birinin bu soruna çalışan bir çözümü varsa, periyodik olarak kontrol edeceğim ve kabul edilen cevabı değiştirebilirim.
Eric Schoonover

1
Bu sadece kabul edilen cevap olmamalı, çünkü sorulan soruya cevap vermiyor. bu soru, bulunana kadar kabul edilmiş bir cevap olmadan kalmalıdır.
7'de shoosh

4
Ve can sıkıcı bir şekilde, cmd.exe'nin fazladan örnekleri sayılmaz. Onlar her değişiklik yeni cmd.exe en geçmesinin öldürmesi gereken var.

6
Bu cevabın olumsuz yorumları ve aşağı işaretlemesi, yığın taşmasının zaman zaman nasıl olduğunu gösterir. Kev doğru cevabı verdi. Sadece beğenmemeniz için işaretlemek için bir sebep yok.
David Arno

Kev kesinlikle soruyu cevaplıyor. Soru, yerleşik bir çözüm olmadığıdır.
Eric Schoonover

40

Sonunda daha kolay bir çözüm bulmadan önce bu cevaba rastladım.

explorer.exeGörev Yöneticisi'nde yeniden başlatmanız yeterlidir .

Test etmedim, ama aynı zamanda komut istemini tekrar açmanız gerekebilir.

Timo Huovinen'e kredi : Başarılı bir şekilde kurulmuş olmasına rağmen düğüm tanınmıyor (bu size yardımcı olduysa, lütfen bu adamın yorum kredisini verin).


Buraya geldiğim için görsel stüdyoya harici bir araç eklemeye çalışıyordum, böylece bu blogda açıklandığı gibi çözümümün kökünde bir komut istemi açabildim : neverindoubtnet.blogspot.com/2012/10/… ... ve Benzer sorunlarım vardı ... "git" i yol değişkenimde görmeye çalışıyordum. Git dizinlerini PATH değişkenine ekledim, ancak Visual Studio'dan açacağım komut isteminde görünmezlerdi. Basit çözüm Visual Studio'yu yeniden başlatmaktı. Daha sonra PATH değişkenine yeni eklemeler cmd cinsinden görüldü.
David Barrows

4
Bu çözüm Windows 10'da bana yardımcı oluyor
ganchito55

7
Soru şuydu: " CMD'yi yeniden başlatmadan bunu yapabilecek bir komut var mı ?"
Florian F

Görev yöneticisinden Tamam ben explorer.exe yeniden başlatmak mümkün değildi, sadece onu bitirmek. Yaptım ama görev çubuğum bozuldu. Explorer'ı başlatmak için; exe gerçekten çok basit. Hadi "Ctrl + üst karakter + kaçış" -> dosya -> "yeni görevi yerine getir" -> "explorer.exe" benim için çalıştı. Ve evet, sonuçta env var yeni cmd penceresinde kullanıldı.
Oscar

İyi çözüm, teşekkürler! @Oscar'ın yorumunu genişletmek ve adreslemek için: cmdYönetici olarak bir pencere başlatın . Komutu kullanın taskkill /f /im explorer.exe && explorer.exe. Bu, explorer.exe işlemini öldürecek ve yeniden başlatacaktır.
S3DEV

32

Windows 7'de çalışır: SET PATH=%PATH%;C:\CmdShortcuts

echo% PATH% yazarak test edildi ve işe yaradı, iyi. ayrıca yeni bir cmd açarsanız, artık bu sinir bozucu yeniden başlatmaya gerek yok :)


1
Benim için "yeni cmd" için çalışmıyor (Win7 x64). Ekran videosuna
Igor

26
Bu, sorulan soruyu çözmez ve çözmemelidir. Orijinal soru, bir ortam değişkeninin o terminalin dışında ayarlanan bir değere nasıl yenileneceği.
csauve

Bu soruya cevap vermese de, en iyi çalışma çözümünün yarısını sağlar. Bunu kullanıyorum - hangi değişkeni ayarlıyorsam - o zaman kontrol panelini açıyorum ve çevresel değişkeni global olarak ekliyorum. Kullanmayı sevmiyorum setxçünkü değiştirilmiş değişkenlere sahip olabilen mevcut ortamı devralır ve kalıcı olarak istediğim şeyi değil. Bu şekilde yapmak, değişkenleri kullanmak için konsolu yeniden başlatmaktan kaçınmamı sağlarken, gelecekte küresel olarak kullanılabilir olmama sorununu da önler.
dgo

25

"Setx" kullanın ve cmd istemini yeniden başlatın

Bu iş için " setx " adlı bir komut satırı aracı var . Bu için olduğunu okuma ve yazma env değişkenleri. Değişkenler komut penceresi kapatıldıktan sonra da devam eder.

"Programlama veya komut dosyası oluşturma gerektirmeden, kullanıcı veya sistem ortamında ortam değişkenleri oluşturur veya değiştirir. Setx komutu ayrıca kayıt defteri anahtarlarının değerlerini alır ve bunları metin dosyalarına yazar."

Not: Bu araç tarafından oluşturulan veya değiştirilen değişkenler gelecekteki komut pencerelerinde kullanılabilir, ancak geçerli CMD.exe komut penceresinde bulunmaz. Yani, yeniden başlatmalısınız.

Eğer setxeksik:


Veya kayıt defterini değiştirin

MSDN diyor ki:

Sistem ortam değişkenlerini programlı olarak eklemek veya değiştirmek için, bunları HKEY_LOCAL_MACHINE \ System \ CurrentControlSet \ Control \ Session Manager \ Environment kayıt defteri anahtarına ekleyin , ardından lParam " Environment " dizesine ayarlanmış bir WM_SETTINGCHANGE iletisi yayınlayın .

Bu, kabuk gibi uygulamaların güncellemelerinizi almasını sağlar.


1
Bir ortam değişkenini okumak için setx'in nasıl kullanılacağını genişletebilir misiniz? Çeşitli belgelerin üstesinden geldim ve görmüyorum. : - /
Mark Ribau

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

3
geçerli sistem ortamı: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment\VARIABLEmevcut kullanıcı ortamı: HKEY_CURRENT_USER\Environment\VARIABLE
Mark Ribau

5
setx /? "Yerel bir sistemde, bu araç tarafından oluşturulan veya değiştirilen değişkenler gelecekteki komut pencerelerinde kullanılabilir, ancak geçerli CMD.exe komut penceresinde kullanılamaz." OP mevcut cmd'yi güncellemek istedi.
Superole

4
Alıcı dikkat! Eğer bir varsa özellikle uzun %PATH%sonra setx1024 bayt bu kesecek! Ve aynen böyle, akşamı kayboldu
FaCE

15

Bu işlevi çağırmak benim için çalıştı:

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

8
ve tüm programlar bu mesajı dinlemez (aslında çoğu muhtemelen
duymaz

Hayır, GUI olmayan programlar için de çalışır. Dinleme programlarına gelince ... yeniden başlatılan bir programın güncellenmiş ortamı almasını sağlama sorunu ve bu size bunu verir.
user2023370

11

Ben geldi en iyi yöntem sadece bir Kayıt Defteri sorgusu yapmak oldu. İşte benim örneğim.

Örneğimde, yeni ortam değişkenleri ekleyen bir Batch dosyası kullanarak bir yükleme yaptım. Kurulum tamamlanır tamamlanmaz bununla bir şeyler yapmam gerekiyordu, ancak bu yeni değişkenlerle yeni bir süreç ortaya çıkaramadı. Ben başka bir explorer pencere yumurtlama test ve cmd.exe geri çağırdı ve bu çalıştı ama Vista ve Windows 7, Explorer sadece tek bir örnek olarak ve normalde giriş kişi olarak çalışır. Ben yönetici krediler için ihtiyacım çünkü bu otomasyon ile başarısız olacaktır yerel sistemden veya kutuda yönetici olarak çalıştırılmaksızın işleri yapın. Bunun sınırlaması, yol gibi şeyleri işlememesidir, bu sadece basit çevre değişkenleri üzerinde çalıştı. Bu, bir dizine (boşluklarla) geçmek ve .exes vb. Çalıştıran dosyalara kopyalamak için bir toplu iş kullanmamı sağladı.

Yeni Toplu İş için Orjinal Toplu İş çağrıları:

testenvget.cmd SDROOT (veya değişken ne olursa olsun)

@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

Ayrıca farklı fikirlerden ortaya çıkardığım başka bir yöntem daha var. Lütfen aşağıya bakın. Bu temelde en yeni yol değişkenini kayıt defterinden alacaktır, ancak bu, kayıt defteri sorgusunun kendi içinde değişkenler vereceği için bir dizi soruna neden olacaktır, bu, her yerde bu çalışmayacak bir değişken olduğu anlamına gelir. temel olarak yolu ikiye katlayın. Çok kötü. Daha perfered yöntem yapmak olurdu: Set Path =% Path%; C: \ Program Files \ Software .... \

Yeni toplu iş dosyası ne olursa olsun, lütfen dikkatli olun.

@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

Geçerli oturum için yeniden başlatmadan yola bir değişken eklemenin en kolay yolu komut istemini açmak ve yazmaktır:

PATH=(VARIABLE);%path%

düğmesine basın enter.

değişkeninizin yüklenip yüklenmediğini kontrol etmek için şunu yazın

PATH

düğmesine basın enter. Ancak, değişken siz yeniden başlatana kadar yolun yalnızca bir parçası olacaktır.


benim için çalışmadı, yol değişkenindeki ikili dosyalara
erişemedi

7

Bunu, belirtilen bir işlemin içindeki Ortam Tablosunun üzerine yazarak yapmak mümkündür.

Kavramın bir kanıtı olarak, sadece bir cmd.exe işleminde tek bir (bilinen) ortam değişkenini düzenleyen bu örnek uygulamayı yazdım:

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;
}

Örnek çıktı:

>set ENVTEST=abc

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

>set ENVTEST
ENVTEST=def

notlar

Bu yaklaşım aynı zamanda güvenlik kısıtlamalarıyla da sınırlı olacaktır. Hedef daha yüksek bir konumda veya daha yüksek bir hesapta (SYSTEM gibi) çalıştırılırsa, belleğini düzenleme iznimiz olmazdı.

Bunu 32 bit bir uygulamaya yapmak istiyorsanız, yukarıdaki sabit kodlu ofsetler sırasıyla 0x10 ve 0x48 olarak değişecektir. Bu aralıklar (WinDbg örneğin bir hata ayıklayıcı _PEB ve _RTL_USER_PROCESS_PARAMETERS yapılar damping bulunabilir dt _PEBve dt _RTL_USER_PROCESS_PARAMETERS)

Kavram kanıtını OP'nin ihtiyaç duyduğu bir şeye dönüştürmek için, mevcut sistem ve kullanıcı ortamı değişkenlerini (@ tsadok'un cevabı ile belgelendiği gibi) numaralandırır ve tüm ortam tablosunu hedef sürecin hafızasına yazar.

Düzenleme: Ortam bloğunun boyutu da _RTL_USER_PROCESS_PARAMETERS yapısında saklanır, ancak bellek işlemin yığınına ayrılır. Yani harici bir süreçten, onu yeniden boyutlandırma ve büyütme yeteneğimiz olmazdı. Ortam depolama için hedef işlemde ek bellek ayırmak için VirtualAllocEx kullanarak oynadım ve tamamen yeni bir tablo ayarlamak ve okumak mümkün. Ne yazık ki, adres artık yığına işaret etmediği için ortamı normal yollardan değiştirme denemesi çökecek ve yanacaktır (RtlSizeHeap'te çökecektir).


6

Ortam değişkenleri HKEY_LOCAL_MACHINE \ SYSTEM \ ControlSet \ Control \ Session Manager \ Environment içinde tutulur.

Path gibi yararlı env değişkenlerinin çoğu REG_SZ olarak depolanır. REGEDIT dahil kayıt defterine erişmenin birkaç yolu vardır:

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

Çıktı sihirli sayılarla başlar. Bu nedenle find komutuyla aramak için yazılması ve yeniden yönlendirilmesi gerekir:type <filename> | findstr -c:\"Path\"

Bu nedenle, geçerli komut oturumunuzdaki yol değişkenini sistem özelliklerinde bulunanlarla yenilemek istiyorsanız, aşağıdaki toplu komut dosyası iyi çalışır:

RefreshPath.cmd:

    @Eko kapalı

    REM Bu çözüm, kayıt defterinden okumak için yükseltme ister.

    Varsa% temp% \ env.reg del% temp% \ env.reg / q / f

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

    yoksa% temp% \ env.reg (
       echo "Geçici konuma kayıt defteri yazılamıyor"
       çıkış 1
       )

    SETLOCAL EnableDelayedExpansion

    / f "belirteçleri = 1,2 * delims ==" %% i in ('tür% temp% \ env.reg ^ | findstr -c: \ "Yol \" =') yap (
       setath = %% ~ j
       echo! upath: \\ = \! >% Temp% \ NEWPATH
       )

     ENDLOCAL

     / f "belirteçleri = *" için %% i (% temp% \ newpath) yolunu ayarla = %% i

5
Ortam değişkenleri vardır değil kayıt defterinde tutulur. Kayıt defterinde tutulan, Windows Gezgini (yeniden) gibi programların bildirildiğinde çevre değişkenlerini oluşturduğu bir şablondur . Gerçek ortam değişkenleri işlem başına olup her işlemin kendi adres alanında saklanır, başlangıçta ana işleminden devralınır ve daha sonra işlemin kaprisinde değiştirilebilir.
JdeBP

5

Yönetici olarak yeni bir komut istemi açmayı deneyin. Bu benim için Windows 10'da çalıştı. (Bunun eski bir cevap olduğunu biliyorum, ama bunu paylaşmak zorundaydım çünkü sadece bunun için bir VBS betiği yazmak zorunda kaldım).


5

Explorer'ı yeniden başlatmak bunu benim için yaptı, ancak sadece yeni cmd terminalleri için.

Yolu ayarladığım terminal zaten yeni Path değişkenini görebiliyordu (Windows 7'de).

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

5

Kafa karıştırıcı olan şey, cmd'yi başlatmak için birkaç yer olması olabilir. Benim durumumda ben koştu kaşif pencerelerden cmd ve çevre değişmedi değişkenler başlatırken ederken "run" dan cmd ortamı (pencereler tuşu + r) değişkenler değiştirildi .

Benim durumumda , görev çubuğundan windows explorer işlemini öldürmek ve sonra görev yöneticisinden yeniden başlatmak zorunda kaldım .

Bunu yaptıktan sonra yeni bir ortam değişkenine Windows Gezgini'nden çıkan bir cmd'den erişebildim.


3

Toplu komut dosyalarında aşağıdaki kodu kullanıyorum:

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

Kullanarak SET sonra setx o komut penceresi yeniden başlatmadan doğrudan "yerel" değişkeni kullanmak mümkündür. Ve bir sonraki çalışmada, çevre değişkeni kullanılacaktır.


Yaptığın şeyi alırken, büyük olasılıkla paralel komut dosyaları için bir şeyler istiyor, bir komut dosyası küreselleri ayarlarken diğeri onları okur. Aksi takdirde, setx'i dahil etmenin bir anlamı yoktur, set yeterli olacaktır.
JasonXA

3

Anonim bir korkak cevabında yayınlandığı gibi chocolatey tarafından takip edilen yaklaşımı beğendim, çünkü saf bir toplu yaklaşım. Bununla birlikte, geçici bir dosya ve bazı geçici değişkenler etrafta kalır. Kendim için daha temiz bir versiyon yaptım.

refreshEnv.batÜzerinde bir yerde bir dosya oluşturun PATH. Konsol ortamınızı çalıştırarak yenileyin 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.

bu% CLIENTNAME% için de geçerli mi? - benim için çalışmadı - stackoverflow.com/questions/37550160/…
Igor L.

% CLIENTNAME% ortamımda mevcut değil ve sorunuzu okuyarak, bunun harici bir süreç tarafından ayarlandığını varsayacağım. (Bir işlem bir alt işlem başlattığında, o çocuğun ortamını ayarlayabilir.) Gerçek ortam değişkenlerinin bir parçası olmadığından, bu komut dosyası tarafından güncelleştirilmez.
DieterDP

Merhaba @ DieterDP, çözümünüz benim için çalışıyor! Windows 10'u 64 bit makinede kullanıyorum. Bir hata alıyorum: "HATA: Sistem belirtilen kayıt defteri anahtarını veya değerini bulamadı." Bununla birlikte, ortam değişkenlerinin güncellenmesi başarılıdır. Hata nereden geliyor?
K.Mulier

Aslında kendimi test etmeden söylemek zor, ama W10 üzerindeki kayıt defteri yapısının biraz farklı olabileceğini tahmin ediyorum. İsterseniz, komut satırında komutları yürüterek hatayı avlamayı deneyin.
DieterDP

2

Değiştirmek istediğiniz sadece bir (veya birkaç) spesifik varyasyonu ilgilendiriyorsa, en kolay yolun bir çözüm olduğunu düşünüyorum : sadece ortamınızda VE mevcut konsol oturumunuzda

  • Set, var olan oturumu değiştirir
  • SetX var olanı ortama koyar, fakat mevcut oturumunuza DEĞİL

Benim Maven Java7 Java8 (her ikisi de env vars olan) değiştirmek için bu basit toplu komut dosyası var Her zaman ' j8 ' çağırabilir ve benim konsol ve çevre içinde benim JAVA_HOME var böylece toplu klasör PATH var değişti:

j8.bat:

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

Şimdiye kadar bu işi en iyi ve en kolay buluyorum. Muhtemelen bunun tek bir komutta olmasını istiyorsunuz, ancak sadece Windows'ta yok ...


2

Birkaç yıldır kullandığım çözüm:

@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

Düzenleme: Woops, işte güncellenmiş sürüm.


Cevabını beğendim. İşte benim soruya aynı cevabı yayınla stackoverflow.com/q/61473551/1082063 ve ben gibi kabul edecek cevap. Teşekkürler.
David I. McIntosh

1

Kev'nın dediği gibi düz bir yol yok. Çoğu durumda, başka bir CMD kutusu oluşturmak daha kolaydır. Daha rahatsız edici bir şekilde, çalışan programlar da değişikliklerin farkında değildir (IIRC, bu değişiklikten haberdar olmak için izlemek için bir yayın mesajı olabilir).

Daha da kötüsü: Windows'un eski sürümlerinde, değişiklikleri hesaba katmak için oturumu kapatıp yeniden oturum açmanız gerekiyordu ...


1

PATH değişkenine eklemek için bu Powershell betiğini kullanıyorum . Küçük bir ayarlama ile sizin durumunuzda da çalışabileceğine inanıyorum.

#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'da bile oldukça ilginç olan bu soruyu yayınladığınız için teşekkür ederiz (Gerçekten de, yukarıda belirtildiği gibi tek bir örnek olduğu için kabuk cmd'sini yenilemek kolay değildir), çünkü pencerelerde ortam değişkenlerini yenilemek, birçok otomasyon görevini komut satırını el ile yeniden başlatmanız gerekir.

Örneğin, yazılımı düzenli olarak yeniden yüklediğimiz çok sayıda makineye dağıtıp yapılandırmak için kullanıyoruz. Ve yazılımımızın konuşlandırılması sırasında komut satırını yeniden başlatmanın çok pratik olmayacağını ve mutlaka hoş olmayan geçici çözümler bulmamızı gerektirdiğini itiraf etmeliyim. Sorunumuza geçelim. Aşağıdaki gibi devam ediyoruz.

1 - Buna benzer bir powershell betiği çağıran bir toplu komut dosyamız var

[dosya: görev.cmd] .

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

2 - Bundan sonra refresh.ps1 betiği, kayıt defteri anahtarlarını (GetValueNames () vb.) Kullanarak ortam değişkenlerini yeniler. Daha sonra, aynı powershell betiğinde, kullanılabilir olan yeni ortam değişkenlerini çağırmamız gerekir. Örneğin, tipik bir durumda, nodeJS'yi daha önce cmd ile sessiz komutları kullanarak kurduysak, işlev çağrıldıktan sonra, aynı oturumda aşağıdaki gibi belirli paketleri kurmak için doğrudan npm'yi çağırabiliriz.

[dosya: 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 betiği bittiğinde, cmd betiği diğer görevlerle devam eder. Şimdi, akılda tutulması gereken bir şey, görev tamamlandıktan sonra, cmd'nin yeni ortam değişkenlerine hala erişimi yoktur, powershell betiği kendi oturumunda bunları güncellemiş olsa bile. Bu yüzden elbette cmd ile aynı komutları çağırabilecek powershell betiğinde gerekli tüm görevleri yapıyoruz.


0

Düzenle: Bu yalnızca, yaptığınız ortam değişiklikleri bir toplu iş dosyası çalıştırmanın bir sonucuysa çalışır.

Bir toplu iş dosyası ile SETLOCALbaşlarsa ENDLOCAL, toplu iş çıkmadan önce aramayı unutsanız veya beklenmedik bir şekilde sonlandırsa bile, çıkışta her zaman orijinal ortamınıza geri döner .

Yazdığım hemen hemen her toplu iş dosyası başlar, SETLOCALçünkü çoğu durumda ortam değişikliklerinin yan etkilerinin kalmasını istemiyorum. Belirli ortam değişkeni değişikliklerinin toplu iş dosyasının dışında yayılmasını istediğiniz durumlarda, sonuncum ENDLOCALşöyle görünür:

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

-1

Bunu çözmek için, BOTH setx ve set kullanarak ortam değişkenini değiştirdim ve sonra explorer.exe'nin tüm örneklerini yeniden başlattım. Bu şekilde daha sonra başlatılan herhangi bir işlem yeni ortam değişkenine sahip olacaktır.

Bunu yapmak için toplu iş komut dosyası:

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

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

Bu yaklaşımla ilgili sorun şu anda açık olan tüm explorer pencerelerinin kapatılmasıdır, bu muhtemelen kötü bir fikirdir - Ama bunun neden gerekli olduğunu öğrenmek için Kev tarafından gönderilen gönderiye bakın.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.