Esiste un comando per aggiornare le variabili di ambiente dal prompt dei comandi in Windows?


481

Se modifico o aggiungo una variabile di ambiente, devo riavviare il prompt dei comandi. Esiste un comando che potrei eseguire per fare ciò senza riavviare CMD?


38
In realtà, ogni programma che deve vederli deve essere riavviato. L'ambiente viene copiato nella memoria del processo all'avvio e quindi non ha più alcun collegamento con gli envvar definiti dal sistema.
Joey,

15
dopo aver letto questi, mi sono reso conto che non c'è un cucchiaio ;) nel mondo reale, riavvii semplicemente cmd.
n611x007,

Non è un comando, quindi non proprio una risposta, ma c'è supporto per l'utilizzo dell'API Win32 se ho letto correttamente quanto segue: support.microsoft.com/en-us/help/104011/… Shoud essere in grado di compilare quella riga in un programma C semplice ed eseguirlo seguendo gli aggiornamenti delle variabili d'ambiente.
Charles Grunwald,

WM_SETTINGCHANGE (l'API win32 menzionato da @CharlesGrunwald) non funziona per le finestre cmd.exe secondo questo thread: github.com/chocolatey/choco/issues/1589 - è la ragione per cui hanno scritto il comando refreshenv
davr

Risposte:


137

È possibile acquisire le variabili di ambiente di sistema con uno script vbs, ma è necessario uno script bat per modificare effettivamente le variabili di ambiente correnti, quindi questa è una soluzione combinata.

Creare un file denominato resetvars.vbscontenente questo codice e salvarlo sul percorso:

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

creare un altro nome file resetvars.bat contenente questo codice, stesso percorso:

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

Quando vuoi aggiornare le variabili di ambiente, esegui resetvars.bat


apologetica :

I due problemi principali che ho avuto con questa soluzione erano

un. Non sono riuscito a trovare un modo semplice per esportare le variabili di ambiente da uno script vbs al prompt dei comandi e

b.la variabile d'ambiente PATH è una concatenazione dell'utente e delle variabili PATH di sistema.

Non sono sicuro di quale sia la regola generale per le variabili in conflitto tra utente e sistema, quindi ho scelto di far sì che l'utente sovrascriva il sistema, ad eccezione della variabile PATH che viene gestita in modo specifico.

Uso lo strano meccanismo vbs + bat + bat temporaneo per aggirare il problema dell'esportazione di variabili da vbs.

Nota : questo script non elimina le variabili.

Questo può probabilmente essere migliorato.

AGGIUNTO

Se devi esportare l'ambiente da una finestra cmd a un'altra, usa questo script (chiamiamolo 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

Eseguire exportvars.vbsnella finestra che si desidera esportare da , quindi passare alla finestra che si desidera esportare a , e digitare:

"%TEMP%\resetvars.bat"

2
Forse puoi evitare il file temporaneo usando il costrutto FOR / F "tokens = 1, *" %% c IN ('resetvars.vbs') DO
tzot

2
Come ho detto nella mia risposta "oppure, aggiungere manualmente utilizzando SET nel prompt dei comandi esistente". che è ciò che sta effettivamente facendo. Buona risposta però.
Kev,

2
@itsadok - dato che questa è ora la risposta accettata, dovresti aggiungere una breve spiegazione all'inizio per mettere lo script nel contesto. vale a dire sottolineare che non è possibile propagare una modifica env var a un cmd.exe aperto senza aggiornare manualmente come sopra o riavviando cmd.exe.
Kev,

Lo script gestisce il caso d'uso della modifica globale delle variabili di ambiente in "Risorse del computer ... Variabili di ambiente", ma se una variabile di ambiente viene modificata in un cmd.exe lo script non lo propaga a un altro cmd.exe in esecuzione che penso sia probabilmente uno scenario comune.
Kev,

1
@Keyslinger: In realtà non è possibile. Qualsiasi programma generato può aggiornare il proprio ambiente, ma non quello dell'istanza cmd.exe in esecuzione. Un file batch PU update aggiornare l'ambiente cmd.exe, poiché viene eseguito nella stessa istanza di cmd.exe.
Ben Voigt,

112

Ecco cosa utilizza 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 .

66
+1 Se hai installato Chocolatey, puoi semplicemente eseguire RefreshEnvper ottenere le variabili di ambiente aggiornate nella sessione corrente.
Martin Valgur,

2
Questo è un software di utilità incredibilmente utile, grazie mille per la condivisione.
Sabuncu,

10
Nota: Chocolatey ha spostato i repository e l'ultima versione di questo script è disponibile qui (con alcune correzioni di bug): github.com/chocolatey/choco/blob/master/src/…
Michael Burr,

1
dovrebbe funzionare Powershellanche questo? Sembra funzionare solo cmd.exeper me.
Iraq

1
Funziona per me in PowerShell, @craq. Esecuzione di Windows 10 x64.
Mazunki,

100

Su Windows 7/8/10, puoi installare Chocolatey, che ha uno script per questo built-in.

Dopo aver installato Chocolatey, basta digitare refreshenv.


questa è una risposta valida, sarebbe bello sentire un ragazzo che l'aveva
declassato

2
Che cosa sto facendo di sbagliato? $> refreshenv 'refreshenv' non è riconosciuto come comando interno o esterno, programma eseguibile o file batch.
aclokay,

@aclokay Non sono sicuro. Fornisci maggiori dettagli sulla tua configurazione del sistema per eseguire il debug. Nel frattempo puoi fare riferimento a un problema aperto simile qui. github.com/chocolatey/choco/issues/250
jolly

Neanche per me funziona. Sono su W7 professionale, forse funziona solo in versioni più complete.
anche il

1
Se refreshenv esiste prematuramente il tuo script (come ha fatto per me), puoi invece usare "call RefreshEnv.cmd". (vedi github.com/chocolatey/choco/issues/1461 )
sfiss

59

In base alla progettazione, non esiste un meccanismo incorporato per la propagazione di una variabile di ambiente da aggiungere / modificare / rimuovere in un cmd.exe già in esecuzione, da un altro cmd.exe o da "Risorse del computer -> Proprietà -> Impostazioni avanzate -> Variabili ambientali".

Se si modifica o si aggiunge una nuova variabile di ambiente al di fuori dell'ambito di un prompt dei comandi aperto esistente, è necessario riavviare il prompt dei comandi oppure aggiungere manualmente utilizzando SET nel prompt dei comandi esistente.

L' ultima risposta accettata mostra una soluzione parziale aggiornando manualmente tutte le variabili di ambiente in uno script. Lo script gestisce il caso d'uso della modifica globale delle variabili di ambiente in "Risorse del computer ... Variabili di ambiente", ma se una variabile di ambiente viene modificata in un cmd.exe, lo script non lo propaga a un altro cmd.exe in esecuzione.


Se qualcuno ha una soluzione funzionante a questo problema, effettuerò periodicamente il check-in e potrei cambiare la risposta accettata.
Eric Schoonover,

1
Questa non dovrebbe essere la risposta accettata semplicemente perché non risponde alla domanda posta. questa domanda dovrebbe rimanere senza una risposta accettata fino a quando non viene trovata una.
shoosh

4
E fastidiosamente, le istanze extra di cmd.exe non contano. Devono essere tutti eliminati prima che la modifica si rifletta in ogni nuovo cmd.exe.

6
I commenti negativi e la marcatura verso il basso di questa risposta mostrano talvolta l'overflow dello stack rotto. Kev ha dato la risposta corretta. Solo perché non ti piace non è un motivo per segnarlo.
David Arno,

Kev sicuramente risponde alla domanda. La domanda è che non esiste una soluzione integrata.
Eric Schoonover,

40

Ho trovato questa risposta prima di trovare una soluzione più semplice.

Basta riavviare explorer.exein Task Manager.

Non ho eseguito il test, ma potrebbe anche essere necessario riaprire il prompt dei comandi.

Ringraziamo Timo Huovinen qui: nodo non riconosciuto sebbene installato con successo (se questo ti ha aiutato, per favore, dai credito al commento di quest'uomo).


Sono arrivato qui perché stavo cercando di aggiungere uno strumento esterno a Visual Studio in modo da poter aprire un prompt dei comandi alla radice della mia soluzione, come descritto in questo blog: neverindoubtnet.blogspot.com/2012/10/… ... e Ho avuto problemi simili ... Stavo cercando di far apparire "git" nella mia variabile di percorso. Ho aggiunto le directory git alla variabile PATH ma poi non apparivano nel prompt dei comandi che avrei aperto da Visual Studio. La soluzione semplice era riavviare Visual Studio. Quindi le nuove aggiunte alla variabile PATH erano visibili in cmd.
David Barrows,

4
Questa soluzione mi aiuta in Windows 10
ganchito55,

7
La domanda era: "Esiste un comando che potrei eseguire per farlo senza riavviare CMD ?"
Florian F,

Ok, dal task manager non sono stato in grado di riavviare explorer.exe, finirlo solo. L'ho fatto ma la mia barra delle attività è stata rotta. Per avviare explorer; exe è davvero semplice. Facciamo "Ctrl + shift + escape" -> file -> "esegui nuova attività" -> "explorer.exe" ha funzionato per me. E sì, dopo tutto env var è stato usato nella nuova finestra cmd. Grazie per tutto
Oscar

Buona soluzione, grazie! Per espandere e rispondere al commento di @Oscar: Avviare una cmdfinestra come amministratore. Utilizzare il comando taskkill /f /im explorer.exe && explorer.exe. Questo ucciderà il processo explorer.exe e lo riavvierà.
S3DEV,

32

Funziona su Windows 7: SET PATH=%PATH%;C:\CmdShortcuts

testato digitando echo% PATH% e ha funzionato bene. imposta anche se apri un nuovo cmd, non è più necessario riavviare quei fastidiosi :)


1
Non funziona per "nuovo cmd" per me (Win7 x64). Guarda il video
Igor,

26
Ciò non risolve la domanda che viene posta, né dovrebbe. La domanda originale è come aggiornare una variabile d'ambiente ad un valore che è stato impostato al di fuori di quel terminale.
csauve,

Sebbene questo non risponda alla domanda, fornisce la metà della migliore soluzione di lavoro. Lo uso - per qualsiasi variabile che sto impostando - quindi apro il pannello di controllo e aggiungo la variabile ambientale a livello globale. Non mi piace usare setxperché eredita l'ambiente attuale che può avere variabili che sono state modificate e non ciò che voglio in modo permanente. Farlo in questo modo mi consente di evitare di riavviare la console per utilizzare le variabili, evitando il problema di non averle disponibili a livello globale in futuro.
DGO

25

Utilizzare "setx" e riavviare il prompt cmd

Esiste uno strumento da riga di comando chiamato " setx " per questo lavoro. È per la lettura e la scrittura di variabili env. Le variabili persistono dopo la chiusura della finestra di comando.

"Crea o modifica le variabili di ambiente nell'ambiente utente o di sistema, senza richiedere programmazione o script. Il comando setx recupera anche i valori delle chiavi di registro e li scrive in file di testo".

Nota: le variabili create o modificate da questo strumento saranno disponibili nelle future finestre di comando ma non nella finestra di comando CMD.exe corrente. Quindi, è necessario riavviare.

Se setxmanca:


Oppure modifica il registro

MSDN dice:

Per aggiungere o modificare a livello di codice variabili di ambiente di sistema, aggiungerle alla chiave di registro HKEY_LOCAL_MACHINE \ System \ CurrentControlSet \ Control \ Session Manager \ Environment , quindi trasmettere un messaggio WM_SETTINGCHANGE con lParam impostato sulla stringa " Environment ".

Ciò consente alle applicazioni, come la shell, di raccogliere gli aggiornamenti.


1
Potresti espandere su come usare setx per leggere una variabile d'ambiente? Ho esaminato la documentazione e non la vedo. : - /
Mark Ribau,

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

3
ambiente di sistema attuale: ambiente di HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment\VARIABLEutenti attuale: HKEY_CURRENT_USER\Environment\VARIABLE
Mark Ribau,

5
setx /? dice in una nota: "Su un sistema locale, le variabili create o modificate da questo strumento saranno disponibili nelle future finestre di comando ma non nella finestra di comando CMD.exe corrente ." OP voleva aggiornare l'attuale cmd.
Superole,

4
Compratore stai attento! Se si dispone di un periodo particolarmente lungo %PATH%, allora setxpuò troncare questo a 1024 byte! E proprio così, la sua serata svanì
FaCE il

15

Chiamare questa funzione ha funzionato per me:

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

8
e non tutti i programmi ascoltano questo messaggio (in realtà molti di loro probabilmente non lo fanno)
Rehan Khwaja,

No, funziona anche con programmi non GUI. Per quanto riguarda i programmi di ascolto ... il problema è garantire che un programma riavviato riceva l'ambiente aggiornato, e questo ti dà questo.
user2023370,

11

Il metodo migliore che mi è venuto in mente è stato semplicemente fare una query del Registro di sistema. Ecco il mio esempio

Nel mio esempio ho fatto un'installazione usando un file Batch che ha aggiunto nuove variabili d'ambiente. Avevo bisogno di fare le cose non appena l'installazione era completa, ma non ero in grado di generare un nuovo processo con quelle nuove variabili. Ho provato a generare un'altra finestra di Explorer e richiamato su cmd.exe e questo ha funzionato, ma su Vista e Windows 7, Explorer funziona solo come una singola istanza e normalmente come la persona che ha effettuato l'accesso. Questo fallirebbe con l'automazione poiché ho bisogno dei miei crediti di amministratore per fare le cose indipendentemente dall'esecuzione dal sistema locale o come amministratore sulla scatola. La limitazione a questo è che non gestisce cose come path, questo ha funzionato solo su semplici variabili di ambiente. Questo mi ha permesso di usare un batch per passare a una directory (con spazi) e copiarlo in file eseguendo .exes ed ecc. Questo è stato scritto oggi dalle risorse di maggio su stackoverflow.com

Chiamate batch originali al nuovo batch:

testenvget.cmd SDROOT (o qualunque sia la variabile)

@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

Inoltre c'è un altro metodo che mi è venuto in mente da varie idee diverse. Vedi sotto. Questo in pratica otterrà la variabile di percorso più recente dal registro, tuttavia ciò causerà una serie di problemi perché la query del registro darà variabili in sé, ciò significa che ovunque c'è una variabile che non funzionerà, quindi per combattere questo problema I sostanzialmente raddoppiare il percorso. Molto cattivo. Il metodo più preferito sarebbe quello di fare: Set Path =% Path%; C: \ Programmi \ Software .... \

Indipendentemente da ciò, ecco il nuovo file batch, prestare attenzione.

@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

Il modo più semplice per aggiungere una variabile al percorso senza riavviare per la sessione corrente è aprire il prompt dei comandi e digitare:

PATH=(VARIABLE);%path%

e premere enter.

per verificare se la variabile è stata caricata, digitare

PATH

e premere enter. Tuttavia, la variabile farà solo parte del percorso fino al riavvio.


non ha funzionato per me, impossibile accedere ai binari nella variabile path
user2305193

7

È possibile farlo sovrascrivendo la tabella di ambiente all'interno di un processo specifico stesso.

Come prova del concetto ho scritto questa app di esempio, che ha appena modificato una singola variabile di ambiente (nota) in un processo 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;
}

Uscita campione:

>set ENVTEST=abc

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

>set ENVTEST
ENVTEST=def

Appunti

Questo approccio sarebbe inoltre limitato alle restrizioni di sicurezza. Se il target viene eseguito a una quota più elevata o con un account più elevato (come SYSTEM), non avremmo il permesso di modificare la sua memoria.

Se si desidera eseguire questa operazione su un'app a 32 bit, gli offset con codice fisso sopra cambieranno rispettivamente in 0x10 e 0x48. Questi offset possono essere trovati scaricando le strutture _PEB e _RTL_USER_PROCESS_PARAMETERS in un debugger (ad esempio in WinDbg dt _PEBe dt _RTL_USER_PROCESS_PARAMETERS)

Per modificare la bozza di concetto in una cosa di cui l'OP ha bisogno, enumera semplicemente il sistema corrente e le variabili di ambiente dell'utente (come documentato dalla risposta di @ tsadok) e scrive l'intera tabella di ambiente nella memoria del processo di destinazione.

Modifica: la dimensione del blocco ambiente è anche memorizzata nella struttura _RTL_USER_PROCESS_PARAMETERS, ma la memoria è allocata sull'heap del processo. Quindi da un processo esterno non avremmo la possibilità di ridimensionarlo e ingrandirlo. Ho giocato con l'utilizzo di VirtualAllocEx per allocare memoria aggiuntiva nel processo di destinazione per l'archiviazione dell'ambiente e sono stato in grado di impostare e leggere una tabella completamente nuova. Sfortunatamente, qualsiasi tentativo di modificare l'ambiente dai normali mezzi andrà in crash e brucerà poiché l'indirizzo non punta più all'heap (andrà in crash in RtlSizeHeap).


6

Le variabili di ambiente sono conservate in HKEY_LOCAL_MACHINE \ SYSTEM \ ControlSet \ Control \ Session Manager \ Environment.

Molti degli utili parametri ambientali, come Path, sono memorizzati come REG_SZ. Esistono diversi modi per accedere al registro, incluso REGEDIT:

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

L'output inizia con numeri magici. Quindi per cercarlo con il comando find deve essere digitato e reindirizzato:type <filename> | findstr -c:\"Path\"

Pertanto, se si desidera solo aggiornare la variabile path nella sessione di comando corrente con le proprietà del sistema, il seguente script batch funziona correttamente:

RefreshPath.cmd:

    @echo off

    REM Questa soluzione richiede l'elevazione per poter leggere dal registro.

    se esiste% temp% \ env.reg del% temp% \ env.reg / q / f

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

    se non esiste% temp% \ env.reg (
       echo "Impossibile scrivere il registro nella posizione temporanea"
       uscita 1
       )

    SETLOCAL EnableDelayedExpansion

    per / f "token = 1,2 * delims ==" %% i in ('digitare% temp% \ env.reg ^ | findstr -c: \ "Path \" =') do (
       set upath = %% ~ j
       echo! upath: \\ = \! >% Temp% \ newpath
       )

     ENDLOCAL

     per / f "token = *" %% i in (% temp% \ newpath) imposta path = %% i

5
Le variabili di ambiente non vengono mantenute nel registro. Ciò che è conservato nel registro è un modello , dal quale programmi come Windows Explorer (ri) costruiscono le loro variabili di ambiente quando vengono avvisati di farlo . Le variabili di ambiente effettive sono per processo e sono memorizzate nello spazio degli indirizzi di ciascun processo, inizialmente ereditato dal relativo processo padre e modificabile in seguito per capriccio del processo.
JdeBP

5

Prova ad aprire un nuovo prompt dei comandi come amministratore. Questo ha funzionato per me su Windows 10. (So che questa è una vecchia risposta, ma ho dovuto condividerla perché dover scrivere uno script VBS solo per questo è assurdo).


5

Il riavvio di Explorer ha fatto questo per me, ma solo per i nuovi terminali cmd.

Il terminale su cui ho impostato il percorso potrebbe già vedere la nuova variabile Path (in Windows 7).

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

5

La cosa confusa potrebbe essere che ci sono alcuni punti da cui iniziare il cmd. Nel mio caso ho eseguito cmd da Windows Explorer e le variabili di ambiente non sono cambiate mentre all'avvio di cmd da "run" (tasto Windows + r) le variabili di ambiente sono state modificate .

Nel mio caso ho dovuto semplicemente terminare il processo di Windows Explorer dal Task Manager e quindi riavviarlo nuovamente dal Task Manager .

Una volta fatto ciò, ho avuto accesso alla nuova variabile d'ambiente da un cmd generato da Windows Explorer.


3

Uso il seguente codice nei miei script batch:

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

Usando SET dopo SETX è possibile usare direttamente la variabile "locale" senza riavviare la finestra di comando. E alla prossima corsa, verrà utilizzata la variabile di ambiente.


Mentre ottengo ciò che hai fatto, molto probabilmente vuole qualcosa per gli script paralleli, uno script imposta i globali mentre un altro li legge. Altrimenti, non ha senso coinvolgere setx, set sarebbe sufficiente.
JasonXA,

3

Mi è piaciuto l'approccio seguito da Chocolatey, come indicato nella risposta di un codardo anonimo, poiché è un approccio batch puro. Tuttavia, lascia un file temporaneo e alcune variabili temporanee in giro. Ho realizzato una versione più pulita per me stesso.

Crea un file refreshEnv.batda qualche parte sul tuo PATH. Aggiorna il tuo ambiente console eseguendo 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.

è anche questo per% CLIENTNAME%? - non ha funzionato per me - stackoverflow.com/questions/37550160/…
Igor L.

% CLIENTNAME% non è disponibile nel mio ambiente e, leggendo la tua domanda, suppongo che sia impostato da un processo esterno. (Quando un processo avvia un processo figlio, è in grado di regolare l'ambiente per quel figlio.) Poiché non fa parte delle variabili di ambiente effettive, non verrà aggiornato da questo script.
DieterDP

Ciao @DieterDP, la tua soluzione funziona per me! Sto usando Windows 10 su una macchina a 64 bit. Ottengo un errore: "ERRORE: il sistema non è stato in grado di trovare la chiave o il valore di registro specificati". Tuttavia, l'aggiornamento delle variabili di ambiente ha esito positivo. Da dove viene l'errore?
K.Mulier,

Difficile a dirsi senza effettivamente provarlo da solo, ma immagino che la struttura del registro su W10 possa differire leggermente. Se ne hai voglia, prova a cercare l'errore eseguendo i comandi sulla riga di comando.
DieterDP,

2

Se si tratta solo di uno (o pochi) specifici parametri che si desidera modificare, penso che il modo più semplice sia una soluzione alternativa : basta impostare nel proprio ambiente E nella sessione della console corrente

  • Set inserirà il var nella sessione corrente
  • SetX inserirà var nell'ambiente, ma NON nella sessione corrente

Ho questo semplice script batch per cambiare il mio Maven da Java7 a Java8 (che sono entrambi variegati) La cartella batch è nel mio var PATH, quindi posso sempre chiamare ' j8 ' e all'interno della mia console e nell'ambiente il mio JAVA_HOME var viene cambiato:

j8.bat:

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

Fino ad ora trovo che funzioni meglio e più facilmente. Probabilmente vuoi che questo sia in un solo comando, ma semplicemente non è presente in Windows ...


2

La soluzione che utilizzo da alcuni anni:

@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

Modifica: Woops, ecco la versione aggiornata.


Mi piace la tua risposta. Pubblica la stessa risposta alla mia domanda qui stackoverflow.com/q/61473551/1082063 e io accettarlo come la risposta. Grazie.
David I. McIntosh,

1

Non esiste un modo diretto, come ha detto Kev. Nella maggior parte dei casi, è più semplice generare un'altra casella CMD. Ancora più fastidioso, i programmi in esecuzione non sono a conoscenza dei cambiamenti (anche se IIRC potrebbe esserci un messaggio broadcast da guardare per ricevere una notifica di tali cambiamenti).

È stato peggio: nelle versioni precedenti di Windows, è stato necessario disconnettersi quindi riconnettersi per tenere conto delle modifiche ...


1

Uso questo script Powershell per aggiungere alla variabile PATH . Con un piccolo aggiustamento può funzionare anche nel tuo caso, credo.

#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

Grazie per aver pubblicato questa domanda che è abbastanza interessante, anche nel 2019 (In effetti, non è facile rinnovare il cmd della shell poiché è una singola istanza come menzionato sopra), perché il rinnovo delle variabili di ambiente in Windows consente di eseguire molte attività di automazione senza dover riavviare manualmente la riga di comando.

Ad esempio, lo utilizziamo per consentire la distribuzione e la configurazione del software su un gran numero di macchine che reinstalliamo regolarmente. E devo ammettere che dover riavviare la riga di comando durante la distribuzione del nostro software sarebbe molto poco pratico e ci richiederebbe di trovare soluzioni alternative che non siano necessariamente piacevoli. Andiamo al nostro problema. Procediamo come segue.

1 - Abbiamo uno script batch che a sua volta chiama uno script PowerShell come questo

[file: task.cmd] .

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

2 - Successivamente, lo script refresh.ps1 rinnova le variabili di ambiente utilizzando le chiavi di registro (GetValueNames (), ecc.). Quindi, nello stesso script PowerShell, non ci resta che chiamare le nuove variabili d'ambiente disponibili. Ad esempio, in un caso tipico, se abbiamo appena installato nodeJS prima con cmd usando i comandi silent, dopo che la funzione è stata chiamata, possiamo chiamare direttamente npm per installare, nella stessa sessione, pacchetti particolari come segue.

[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

Una volta terminato lo script PowerShell, lo script cmd continua con altre attività. Ora, una cosa da tenere a mente è che dopo che l'attività è stata completata, cmd non ha ancora accesso alle nuove variabili di ambiente, anche se lo script PowerShell ha aggiornato quelli nella propria sessione. Ecco perché eseguiamo tutte le attività necessarie nello script PowerShell che possono ovviamente chiamare gli stessi comandi di cmd.


0

Modifica: funziona solo se le modifiche all'ambiente che stai facendo sono il risultato dell'esecuzione di un file batch.

Se un file batch inizia con SETLOCAL, si risolverà sempre all'ambiente originale all'uscita anche se si dimentica di chiamareENDLOCAL prima della chiusura del batch o se si interrompe in modo imprevisto.

Quasi tutti i file batch che scrivo iniziano SETLOCALpoiché nella maggior parte dei casi non voglio che gli effetti collaterali delle modifiche all'ambiente rimangano. Nei casi in cui desidero propagare determinate modifiche alle variabili di ambiente all'esterno del file batch, il mio ultimo ENDLOCALaspetto è il seguente:

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

-1

Per risolvere questo problema ho modificato la variabile d'ambiente usando ENTRAMBE setx e set, quindi ho riavviato tutte le istanze di explorer.exe. In questo modo qualsiasi processo avviato successivamente avrà la nuova variabile di ambiente.

Il mio script batch per fare questo:

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

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

Il problema con questo approccio è che tutte le finestre di Explorer attualmente aperte saranno chiuse, il che è probabilmente una cattiva idea - Ma vedi il post di Kev per sapere perché questo è necessario

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.