Apakah ada perintah untuk menyegarkan variabel lingkungan dari command prompt di Windows?

481

Jika saya memodifikasi atau menambahkan variabel lingkungan saya harus memulai kembali command prompt. Apakah ada perintah yang bisa saya jalankan yang akan melakukan ini tanpa me-restart CMD?

Eric Schoonover
sumber
38
Sebenarnya, setiap program yang perlu melihatnya harus di-restart. Lingkungan disalin ke dalam memori proses pada saat startup dan karena itu tidak memiliki koneksi apa pun dengan utusan yang ditentukan sistem lagi.
Joey
15
setelah membaca ini, saya menyadari bahwa tidak ada sendok ;) di dunia nyata, Anda baru saja memulai ulang cmd.
n611x007
Bukan perintah, jadi tidak cukup jawaban, tetapi ada dukungan untuk itu menggunakan Win32 API jika saya membaca yang berikut ini dengan benar: support.microsoft.com/en-us/help/104011/... Harus dapat mengkompilasi baris itu menjadi program C sederhana dan jalankan mengikuti pembaruan variabel lingkungan.
Charles Grunwald
WM_SETTINGCHANGE (api win32 yang disebutkan oleh @CharlesGrunwald) tidak berfungsi untuk jendela cmd.exe sesuai dengan utas ini: github.com/chocolatey/choco/issues/1589 - itulah alasan mereka menulis perintah refreshenv
davr

Jawaban:

137

Anda dapat menangkap variabel lingkungan sistem dengan skrip vbs, tetapi Anda memerlukan skrip kelelawar untuk benar-benar mengubah variabel lingkungan saat ini, jadi ini adalah solusi gabungan.

Buat file bernama resetvars.vbsmengandung kode ini, dan simpan di jalur:

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

buat nama file lain resetvars.bat yang berisi kode ini, lokasi yang sama:

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

Saat Anda ingin me-refresh variabel lingkungan, jalankan saja resetvars.bat


Apologetika :

Dua masalah utama yang saya temukan dengan solusi ini adalah

Sebuah. Saya tidak dapat menemukan cara mudah untuk mengekspor variabel lingkungan dari skrip vbs kembali ke command prompt, dan

b. variabel lingkungan PATH adalah gabungan dari pengguna dan variabel sistem PATH.

Saya tidak yakin apa aturan umum untuk variabel yang saling bertentangan antara pengguna dan sistem, jadi saya memilih untuk membuat sistem menimpa pengguna, kecuali dalam variabel PATH yang ditangani secara khusus.

Saya menggunakan vbs aneh + kelelawar + mekanisme kelelawar sementara untuk mengatasi masalah mengekspor variabel dari vbs.

Catatan : skrip ini tidak menghapus variabel.

Ini mungkin bisa diperbaiki.

TAMBAH

Jika Anda perlu mengekspor lingkungan dari satu jendela cmd ke jendela lain, gunakan skrip ini (sebut saja 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

Jalankan exportvars.vbsdi jendela yang ingin Anda ekspor dari , kemudian beralih ke jendela Anda ingin ekspor ke , dan jenis:

"%TEMP%\resetvars.bat"
itsadok
sumber
2
Mungkin Anda dapat menghindari file sementara menggunakan FOR / F "token = 1, *" %% c IN ('resetvars.vbs') DO construct
tzot
2
Seperti yang saya katakan dalam jawaban saya "atau, tambahkan secara manual menggunakan SET pada prompt perintah yang ada." yang melakukan hal ini secara efektif. Jawaban yang bagus.
Kev
2
@itsadok - karena sekarang ini adalah jawaban yang diterima, Anda harus menambahkan penjelasan singkat di awal untuk meletakkan skrip dalam konteks. yaitu menunjukkan bahwa tidak mungkin untuk menyebarkan perubahan env var ke cmd.exe terbuka tanpa memperbarui secara manual seperti di atas atau dengan me-restart cmd.exe.
Kev
Script menangani kasus penggunaan mengubah variabel lingkungan secara global di "Komputer Saya ... Variabel Lingkungan", tetapi jika variabel lingkungan diubah dalam satu cmd.exe, skrip tidak akan menyebarkannya ke cmd.exe lain yang berjalan yang menurut saya adalah mungkin skenario umum.
Kev
1
@Keyslinger: Itu sebenarnya tidak mungkin. Program apa pun yang muncul dapat memperbarui lingkungannya sendiri, tetapi bukan yang menjalankan contoh cmd.exe. File batch BISA memperbarui lingkungan cmd.exe, karena berjalan dalam instance yang sama dari cmd.exe.
Ben Voigt
112

Inilah yang digunakan 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 .
pengecut anonim
sumber
66
+1 Jika Anda telah memasang Chocolatey, Anda bisa menjalankannya RefreshEnvuntuk mendapatkan variabel lingkungan yang diperbarui ke sesi Anda saat ini.
Martin Valgur
2
Ini adalah perangkat lunak utilitas yang sangat berguna, terima kasih banyak sudah berbagi.
Sabuncu
10
Catatan: Chocolatey telah memindahkan repo dan versi terbaru dari skrip ini dapat ditemukan di sini (dengan beberapa perbaikan bug): github.com/chocolatey/choco/blob/master/src/…
Michael Burr
1
haruskah ini bekerja Powershelljuga? Sepertinya hanya berfungsi cmd.exeuntuk saya.
craq
1
Ini berfungsi untuk saya di PowerShell, @craq. Menjalankan Windows10 x64.
Mazunki
100

Pada Windows 7/8/10, Anda dapat menginstal Chocolatey, yang memiliki skrip untuk bawaan ini.

Setelah menginstal Chocolatey, ketik saja refreshenv.

riang
sumber
ini adalah jawaban yang valid, akan menyenangkan untuk mendengar dari pria yang telah
menurunkannya
2
Apa yang saya lakukan salah? $> refreshenv 'refreshenv' tidak dikenali sebagai perintah internal atau eksternal, program yang dapat dijalankan, atau file batch.
aclokay
@aclokay Tidak yakin. Harap berikan lebih detail tentang konfigurasi sistem Anda untuk debug. Sementara itu, Anda dapat merujuk ke masalah terbuka serupa di sini. github.com/chocolatey/choco/issues/250
jolly
Itu tidak bekerja untuk saya juga. Saya menggunakan W7 profesional, mungkin itu hanya bekerja di versi yang lebih lengkap.
alseether
1
Jika refreshenv ada sebelum waktunya skrip Anda (seperti yang terjadi pada saya), Anda dapat menggunakan "panggil RefreshEnv.cmd" sebagai gantinya. (lihat github.com/chocolatey/choco/issues/1461 )
sfiss
59

Secara desain tidak ada yang dibangun di mekanisme untuk Windows untuk menyebarkan variabel lingkungan add / perubahan / remove ke cmd.exe sudah berjalan, baik dari cmd.exe lain atau dari "My Computer -> Properties -> Advanced Settings -> Variabel Lingkungan ".

Jika Anda memodifikasi atau menambahkan variabel lingkungan baru di luar lingkup prompt perintah terbuka yang ada, Anda harus memulai kembali prompt perintah, atau, secara manual menambahkan menggunakan SET dalam prompt perintah yang ada.

The terbaru Jawaban yang diterima menunjukkan kerja-sekitar parsial secara manual menyegarkan semua variabel lingkungan dalam sebuah naskah. Script menangani kasus penggunaan mengubah variabel lingkungan secara global di "My Computer ... Variabel Lingkungan", tetapi jika variabel lingkungan diubah dalam satu cmd.exe skrip tidak akan menyebarkannya ke cmd.exe lain yang sedang berjalan.

Kev
sumber
Jika ada yang punya solusi untuk masalah ini, saya akan checkin secara berkala dan dapat mengubah jawaban yang diterima.
Eric Schoonover
1
Ini seharusnya bukan jawaban yang diterima hanya karena itu tidak menjawab pertanyaan yang diajukan. pertanyaan ini harus tetap tanpa jawaban yang diterima sampai ditemukan.
shoosh
4
Dan yang menjengkelkan, contoh tambahan cmd.exe tidak masuk hitungan. Mereka semua harus dibunuh sebelum perubahan tercermin dalam cmd.exe baru.
6
Komentar negatif dan penandaan ke bawah dari jawaban ini menunjukkan seberapa sering stack overflow pecah. Kev telah memberikan jawaban yang benar. Hanya karena Anda tidak suka, tidak ada alasan untuk menandainya.
David Arno
Kev pasti menjawab pertanyaan itu. Pertanyaannya adalah tidak ada solusi bawaan.
Eric Schoonover
40

Saya menemukan jawaban ini sebelum akhirnya menemukan solusi yang lebih mudah.

Cukup restart explorer.exe di Task Manager.

Saya tidak menguji, tetapi Anda mungkin juga perlu membuka kembali command prompt Anda.

Kredit untuk Timo Huovinen di sini: Node tidak dikenali meskipun berhasil dipasang (jika ini membantu Anda, silakan berikan kredit komentar orang ini).

wharding28
sumber
Saya tiba di sini karena saya mencoba menambahkan alat eksternal ke visual studio sehingga saya dapat membuka command prompt di root solusi saya, seperti yang dijelaskan dalam blog ini: neverindoubtnet.blogspot.com/2012/10/… ... dan Saya memiliki masalah serupa ... Saya mencoba membuat "git" muncul di variabel path saya. Saya menambahkan direktori git ke variabel PATH tetapi kemudian mereka tidak akan muncul di command prompt yang akan saya buka dari Visual Studio. Solusi sederhana adalah me-restart Visual Studio. Kemudian tambahan baru untuk variabel PATH terlihat dalam cmd.
David Barrows
4
Solusi itu membantu saya di Windows 10
ganchito55
7
Pertanyaannya adalah: "Apakah ada perintah yang bisa saya jalankan yang akan melakukan ini tanpa memulai ulang CMD ?"
Florian F
Oke dari task manager saya tidak dapat me-restart explorer.exe, hanya menyelesaikannya. Saya melakukannya tetapi bilah tugas saya telah rusak. Untuk memulai explorer; exe itu sangat sederhana. Mari "Ctrl + shift + escape" -> file -> "jalankan tugas baru" -> "explorer.exe" melakukan pekerjaan untuk saya. Dan ya, setelah semua env var telah digunakan di jendela cmd baru. Terima kasih untuk semuanya
Oscar
Solusi bagus, terima kasih! Untuk memperluas dan mengatasi komentar @scar: Mulai cmdjendela sebagai Administrator. Gunakan perintah taskkill /f /im explorer.exe && explorer.exe. Ini akan mematikan proses explorer.exe dan memulai kembali.
S3DEV
32

Ini bekerja di windows 7: SET PATH=%PATH%;C:\CmdShortcuts

diuji dengan mengetik echo% PATH% dan berhasil, baik. juga mengatur jika Anda membuka cmd baru, tidak perlu lagi reboot yang sial :)

kristofer månsson
sumber
1
Tidak bekerja untuk "cmd baru" untuk saya (Win7 x64). Lihat screenvideo
Igor
26
Ini tidak menyelesaikan pertanyaan yang diajukan, juga tidak seharusnya. Pertanyaan aslinya adalah bagaimana me-refresh variabel lingkungan ke nilai yang telah ditetapkan outisde dari terminal itu.
csauve
Meskipun ini tidak menjawab pertanyaan, itu memberikan setengah dari solusi kerja terbaik. Saya menggunakan ini - untuk variabel apa pun yang saya atur - maka saya membuka panel kontrol dan menambahkan variabel lingkungan secara global. Saya tidak suka menggunakan setxkarena mewarisi lingkungan saat ini yang mungkin memiliki variabel yang telah dimodifikasi dan bukan yang saya inginkan secara permanen. Melakukannya dengan cara ini memungkinkan saya untuk menghindari me-restart konsol untuk menggunakan variabel, sambil menghindari masalah karena tidak tersedia konsol secara global di masa depan.
Lakukan
25

Gunakan "setx" dan restart prompt cmd

Ada alat baris perintah bernama " setx " untuk pekerjaan ini. Ini untuk membaca dan menulis variabel env. Variabel tetap ada setelah jendela perintah telah ditutup.

"Membuat atau memodifikasi variabel lingkungan di lingkungan pengguna atau sistem, tanpa memerlukan pemrograman atau skrip. Setx Perintah juga mengambil nilai-nilai kunci registri dan menulisnya ke file teks."

Catatan: variabel yang dibuat atau dimodifikasi oleh alat ini akan tersedia di jendela perintah di masa mendatang tetapi tidak di jendela perintah CMD.exe saat ini. Jadi, Anda harus memulai ulang.

Jika setxtidak ada:


Atau modifikasi registri

MSDN mengatakan:

Untuk menambahkan atau memodifikasi variabel lingkungan sistem secara program, tambahkan ke HKEY_LOCAL_MACHINE \ System \ CurrentControlSet \ Control \ Session Manager \ kunci registri Lingkungan , lalu siarkan pesan WM_SETTINGCHANGE dengan lParam disetel ke string " Lingkungan ".

Ini memungkinkan aplikasi, seperti shell, untuk mengambil pembaruan Anda.

Jens A. Koch
sumber
1
Bisakah Anda mengembangkan cara menggunakan setx untuk membaca variabel lingkungan? Saya telah membaca berbagai dokumentasi dan saya tidak melihatnya. : - /
Mark Ribau
2
setx VARIABLE -k "HKEY_LOCAL_MACHINE \ Software \ Microsoft \ WindowsNT \ CurrentVersion \ CurrentVersion" echo% VARIABLE%
Jens A. Koch
3
lingkungan sistem saat ini: lingkungan HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment\VARIABLEpengguna saat ini: HKEY_CURRENT_USER\Environment\VARIABLE
Mark Ribau
5
setx /? mengatakan dalam sebuah catatan: "Pada sistem lokal, variabel yang dibuat atau dimodifikasi oleh alat ini akan tersedia di jendela perintah di masa depan tetapi tidak di jendela perintah CMD.exe saat ini ." OP ingin memperbarui cmd saat ini.
Superole
4
Pembeli Waspadalah! Jika Anda memiliki panjang tertentu %PATH%maka setxdapat memotong ini hingga 1024 byte! Dan begitu saja, malamnya menghilang
FaCE
15

Memanggil fungsi ini berhasil bagi saya:

VOID Win32ForceSettingsChange()
{
    DWORD dwReturnValue;
    ::SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, (LPARAM) "Environment", SMTO_ABORTIFHUNG, 5000, &dwReturnValue);
}
Brian Weed
sumber
8
dan tidak semua program mendengarkan pesan ini (bahkan sebagian besar dari mereka mungkin tidak)
Rehan Khwaja
Tidak, ini juga berfungsi untuk program non-GUI. Adapun program mendengarkan ... masalah jika memastikan bahwa program yang dimulai kembali akan menerima lingkungan yang diperbarui, dan ini memberi Anda itu.
user2023370
11

Metode terbaik yang saya temukan adalah dengan hanya melakukan permintaan Registry. Ini contoh saya.

Dalam contoh saya, saya melakukan instalasi menggunakan file Batch yang menambahkan variabel lingkungan baru. Saya perlu melakukan hal-hal dengan ini segera setelah instalasi selesai, tetapi tidak dapat menelurkan proses baru dengan variabel-variabel baru. Saya menguji memunculkan jendela explorer lain dan memanggil kembali ke cmd.exe dan ini bekerja tetapi pada Vista dan Windows 7, Explorer hanya berjalan sebagai contoh tunggal dan biasanya sebagai orang yang login. Ini akan gagal dengan otomatisasi karena saya perlu admin saya percaya untuk melakukan hal-hal terlepas dari menjalankan dari sistem lokal atau sebagai administrator di kotak. Keterbatasan untuk ini adalah bahwa ia tidak menangani hal-hal seperti path, ini hanya bekerja pada variabel lingkungan sederhana. Ini memungkinkan saya untuk menggunakan batch untuk menuju ke direktori (dengan spasi) dan menyalin file yang menjalankan .exes dan lain-lain. Ini ditulis hari ini dari sumber daya Mei di stackoverflow.com

Panggilan Batch Asli ke Batch baru:

testenvget.cmd SDROOT (atau apa pun variabelnya)

@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

Juga ada metode lain yang saya dapatkan dari berbagai ide berbeda. Silahkan lihat di bawah ini. Ini pada dasarnya akan mendapatkan variabel path terbaru dari registri namun, ini akan menyebabkan sejumlah masalah karena permintaan registri akan memberikan variabel dalam dirinya sendiri, itu berarti di mana-mana ada variabel ini tidak akan berfungsi, jadi untuk mengatasi masalah ini saya pada dasarnya menggandakan jalan. Sangat jahat. Metode yang lebih disukai adalah: Set Path =% Path%; C: \ Program Files \ Software .... \

Apapun di sini adalah file batch baru, harap berhati-hati.

@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
Christopher Holmes
sumber
8

Cara termudah untuk menambahkan variabel ke path tanpa me-reboot untuk sesi saat ini adalah membuka command prompt dan ketik:

PATH=(VARIABLE);%path%

dan tekan enter .

untuk memeriksa apakah variabel Anda dimuat, ketikkan

PATH

dan tekan enter. Namun, variabel hanya akan menjadi bagian dari jalan sampai Anda reboot.

Richard Woodruff
sumber
tidak berfungsi untuk saya, tidak bisa mengakses binari dalam variabel path
user2305193
7

Dimungkinkan untuk melakukan ini dengan menimpa Tabel Lingkungan dalam proses yang ditentukan itu sendiri.

Sebagai bukti konsep, saya menulis aplikasi sampel ini, yang baru saja mengedit satu variabel lingkungan (dikenal) dalam proses 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;
}

Output sampel:

>set ENVTEST=abc

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

>set ENVTEST
ENVTEST=def

Catatan

Pendekatan ini juga akan terbatas pada pembatasan keamanan. Jika target dijalankan pada ketinggian yang lebih tinggi atau akun yang lebih tinggi (seperti SISTEM) maka kami tidak akan memiliki izin untuk mengedit memorinya.

Jika Anda ingin melakukan ini pada aplikasi 32-bit, offset kode keras di atas akan berubah menjadi masing-masing 0x10 dan 0x48. Offset ini dapat ditemukan dengan membuang struktur _PEB dan _RTL_USER_PROCESS_PARAMETERS dalam debugger (misalnya di WinDbg dt _PEBdandt _RTL_USER_PROCESS_PARAMETERS )

Untuk mengubah proof-of-concept menjadi apa yang dibutuhkan OP, itu hanya akan menyebutkan sistem saat ini dan variabel lingkungan pengguna (seperti didokumentasikan oleh jawaban @ tsadok) dan menulis seluruh tabel lingkungan ke dalam memori proses target.

Sunting: Ukuran blok lingkungan juga disimpan dalam struktur _RTL_USER_PROCESS_PARAMETERS, tetapi memori dialokasikan pada tumpukan proses. Jadi dari proses eksternal kita tidak akan memiliki kemampuan untuk mengubah ukurannya dan membuatnya lebih besar. Saya bermain-main dengan menggunakan VirtualAllocEx untuk mengalokasikan memori tambahan dalam proses target untuk penyimpanan lingkungan, dan dapat mengatur dan membaca tabel yang sama sekali baru. Sayangnya setiap upaya untuk mengubah lingkungan dari cara normal akan macet dan terbakar karena alamat tidak lagi menunjuk ke tumpukan (itu akan macet di RtlSizeHeap).

Astaga
sumber
6

Variabel lingkungan disimpan di HKEY_LOCAL_MACHINE \ SYSTEM \ ControlSet \ Control \ Session Manager \ Environment.

Banyak vars berguna, seperti Path, disimpan sebagai REG_SZ. Ada beberapa cara untuk mengakses registri termasuk REGEDIT:

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

Output dimulai dengan angka ajaib. Jadi untuk mencarinya dengan perintah find perlu diketik dan dialihkan:type <filename> | findstr -c:\"Path\"

Jadi, jika Anda hanya ingin me-refresh variabel path di sesi perintah Anda saat ini dengan apa yang ada di properti sistem script batch berikut berfungsi dengan baik:

RefreshPath.cmd:

    @echo mati

    REM Solusi ini meminta elevasi untuk membaca dari registri.

    jika ada% temp% \ env.reg del% temp% \ env.reg / q / f

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

    jika tidak ada% temp% \ env.reg (
       echo "Tidak dapat menulis registri ke lokasi temp"
       keluar 1
       )

    SETLOCAL EnableDelayedExpansion

    untuk / f "token = 1,2 * delims ==" %% i in ('ketik% temp% \ env.reg ^ | findstr -c: \ "Path \" =') lakukan (
       atur = %% ~ j
       echo! upath: \\ = \! >% temp% \ newpath
       )

     ENDLOCAL

     untuk / f "token = *" %% i in (% temp% \ newpath) lakukan set path = %% i
Algonaut
sumber
5
Variabel lingkungan tidak disimpan dalam registri. Apa yang disimpan dalam registri adalah templat , dari mana program-program seperti Windows Explorer (kembali) membangun variabel lingkungan mereka ketika diberitahu untuk melakukannya . Variabel lingkungan aktual adalah per-proses dan disimpan dalam ruang alamat masing-masing proses, awalnya diwarisi dari proses induknya dan dapat dimodifikasi setelahnya sesuai dengan keinginan proses.
JdeBP
5

Coba buka prompt perintah baru sebagai administrator. Ini bekerja untuk saya di Windows 10. (Saya tahu ini adalah jawaban lama, tetapi saya harus membagikan ini karena harus menulis skrip VBS hanya untuk ini tidak masuk akal).

estebro
sumber
5

Restart explorer melakukan ini untuk saya, tetapi hanya untuk terminal cmd baru.

Terminal yang saya setel path sudah dapat melihat variabel Path baru (di Windows 7).

taskkill /f /im explorer.exe && explorer.exe
Vince
sumber
5

Yang membingungkan mungkin ada beberapa tempat untuk memulai cmd dari. Dalam kasus saya, saya menjalankan cmd dari windows explorer dan variabel lingkungan tidak berubah ketika memulai cmd dari "run" (windows key + r) variabel lingkungan diubah .

Dalam kasus saya, saya hanya harus mematikan proses windows explorer dari task manager dan kemudian restart lagi dari task manager .

Setelah saya melakukan ini, saya memiliki akses ke variabel lingkungan baru dari cmd yang muncul dari windows explorer.

Daniel Fensterheim
sumber
3

Saya menggunakan kode berikut dalam skrip batch saya:

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

Dengan menggunakan SET setelah SETX dimungkinkan untuk menggunakan variabel "lokal" secara langsung tanpa memulai kembali jendela perintah. Dan pada proses selanjutnya, variabel lingkungan akan digunakan.

Sebastian
sumber
Sementara saya mendapatkan apa yang telah Anda lakukan, kemungkinan besar dia menginginkan sesuatu untuk skrip paralel, satu skrip menetapkan global sementara yang lain membacanya. Kalau tidak, tidak ada gunanya melibatkan setx, set sudah cukup.
JasonXA
3

Saya menyukai pendekatan yang diikuti oleh cokelat, seperti yang diposting dalam jawaban pengecut anonim, karena itu adalah pendekatan batch murni. Namun, ia meninggalkan file sementara dan beberapa variabel sementara tergeletak di sekitar. Saya membuat versi yang lebih bersih untuk diri saya sendiri.

Buat file di refreshEnv.batsuatu tempat di blog Anda PATH. Refresh lingkungan konsol Anda dengan mengeksekusi 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.
DieterDP
sumber
apakah ini juga untuk% CLIENTNAME%? - tidak bekerja untuk saya - stackoverflow.com/questions/37550160/…
Igor L.
% CLIENTNAME% tidak tersedia di lingkungan saya, dan dengan membaca pertanyaan Anda, saya akan menganggap itu sesuatu yang ditetapkan oleh proses eksternal. (Ketika suatu proses memulai proses anak, itu dapat menyesuaikan lingkungan untuk anak itu.) Karena itu bukan bagian dari variabel lingkungan aktual, itu tidak akan diperbarui oleh skrip ini.
DieterDP
Hai @DieterDP, solusi Anda cocok untuk saya! Saya menggunakan Windows 10 pada mesin 64-bit. Saya mendapatkan kesalahan: "GALAT: Sistem tidak dapat menemukan kunci atau nilai registri yang ditentukan." Namun demikian, pembaruan variabel lingkungan berhasil. Dari mana kesalahan itu berasal?
K.Mulier
Sulit dikatakan tanpa benar-benar mengujinya sendiri, tapi saya menduga struktur registri pada W10 mungkin sedikit berbeda. Jika Anda menyukainya, cobalah memburu kesalahan dengan menjalankan perintah pada baris perintah.
DieterDP
2

Jika hanya menyangkut satu (atau beberapa) vars spesifik yang ingin Anda ubah, saya pikir cara termudah adalah solusinya : cukup atur di lingkungan Anda DAN di sesi konsol Anda saat ini

  • Set akan menempatkan var di sesi Anda saat ini
  • SetX akan menempatkan var di lingkungan, tetapi TIDAK di sesi Anda saat ini

Saya memiliki skrip batch sederhana ini untuk mengubah Maven saya dari Java7 ke Java8 (keduanya env. Vars) Batch-folder berada di PATH var saya sehingga saya selalu dapat memanggil ' j8 ' dan di dalam konsol saya dan di lingkungan var JAVA_HOME saya diubah:

j8.bat:

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

Sampai sekarang saya menemukan ini berfungsi terbaik dan termudah. Anda mungkin ingin ini berada dalam satu perintah, tetapi tidak ada di Windows ...

Jeroen van Dijk-Jun
sumber
2

Solusi yang saya gunakan selama beberapa tahun sekarang:

@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

Sunting: Woops, ini versi terbaru.

Charles Grunwald
sumber
Saya suka jawaban Anda. Memposting jawaban yang sama untuk pertanyaan saya di sini stackoverflow.com/q/61473551/1082063 dan saya akan menerimanya sebagai yang jawabannya. Terima kasih.
David I. McIntosh
1

Tidak ada jalan lurus, seperti kata Kev. Dalam kebanyakan kasus, lebih mudah untuk menelurkan kotak CMD lain. Lebih menjengkelkan, menjalankan program juga tidak menyadari perubahan (meskipun IIRC mungkin ada pesan siaran yang harus diperhatikan untuk diberitahu tentang perubahan tersebut).

Ini lebih buruk: di versi Windows yang lebih lama, Anda harus keluar lalu masuk kembali untuk memperhitungkan perubahan ...

PhiLho
sumber
1

Saya menggunakan skrip Powershell ini untuk menambah variabel PATH . Dengan sedikit penyesuaian, ini dapat bekerja dalam kasus Anda juga, saya percaya.

#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%"
Iulian Dita
sumber
1

Terima kasih telah mengirimkan pertanyaan ini yang cukup menarik, bahkan pada tahun 2019 (Memang, tidak mudah untuk memperbarui shell cmd karena ini adalah contoh tunggal seperti yang disebutkan di atas), karena memperbarui variabel lingkungan di windows memungkinkan untuk menyelesaikan banyak tugas otomatisasi tanpa harus me-restart baris perintah secara manual.

Sebagai contoh, kami menggunakan ini untuk memungkinkan perangkat lunak untuk digunakan dan dikonfigurasi pada sejumlah besar mesin yang kami instal ulang secara teratur. Dan saya harus mengakui bahwa harus me-restart baris perintah selama penyebaran perangkat lunak kami akan sangat tidak praktis dan akan meminta kami untuk menemukan solusi yang tidak selalu menyenangkan. Mari kita selesaikan masalah kita. Kami melanjutkan sebagai berikut.

1 - Kami memiliki skrip batch yang pada gilirannya memanggil skrip PowerShell seperti ini

[file: task.cmd] .

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

2 - Setelah ini, skrip refresh.ps1 memperbarui variabel lingkungan menggunakan kunci registri (GetValueNames (), dll.). Kemudian, dalam skrip powershell yang sama, kita hanya perlu memanggil variabel lingkungan baru yang tersedia. Sebagai contoh, dalam kasus khusus, jika kita baru saja menginstal nodeJS sebelum dengan cmd menggunakan perintah diam, setelah fungsi dipanggil, kita dapat langsung memanggil npm untuk menginstal, dalam sesi yang sama, paket-paket tertentu seperti berikut.

[file: 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

Setelah skrip powershell selesai, skrip cmd melanjutkan dengan tugas lain. Sekarang, satu hal yang perlu diingat adalah bahwa setelah tugas selesai, cmd masih belum memiliki akses ke variabel lingkungan baru, bahkan jika skrip powershell telah memperbarui mereka dalam sesi sendiri. Itulah sebabnya kami melakukan semua tugas yang diperlukan dalam skrip powershell yang dapat memanggil perintah yang sama seperti cmd, tentu saja.

Andy McRae
sumber
0

Sunting: ini hanya berfungsi jika perubahan lingkungan yang Anda lakukan adalah akibat menjalankan file batch.

Jika file batch dimulai dengan SETLOCALitu maka akan selalu terurai kembali ke lingkungan asli Anda saat keluar bahkan jika Anda lupa menelepon ENDLOCALsebelum batch keluar, atau jika dibatalkan secara tak terduga.

Hampir setiap file batch yang saya tulis dimulai dengan SETLOCALkarena dalam kebanyakan kasus saya tidak ingin efek samping dari perubahan lingkungan tetap ada. Dalam kasus di mana saya ingin perubahan variabel lingkungan tertentu untuk menyebar di luar file batch maka yang terakhir saya ENDLOCALterlihat seperti ini:

ENDLOCAL & (
  SET RESULT1=%RESULT1%
  SET RESULT2=%RESULT2%
)
bangsal
sumber
-1

Untuk mengatasi ini saya telah mengubah variabel lingkungan menggunakan KEDUA setx dan set, dan kemudian restart semua contoh explorer.exe. Dengan cara ini, setiap proses yang dimulai kemudian akan memiliki variabel lingkungan baru.

Skrip batch saya untuk melakukan ini:

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

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

Masalah dengan pendekatan ini adalah bahwa semua jendela explorer yang saat ini dibuka akan ditutup, yang mungkin merupakan ide yang buruk - Tetapi lihat posting oleh Kev untuk mempelajari mengapa ini diperlukan

Jens Hykkelbjerg
sumber