¿Hay un comando para actualizar las variables de entorno desde el símbolo del sistema en Windows?

481

Si modifico o agrego una variable de entorno, tengo que reiniciar el símbolo del sistema. ¿Hay algún comando que pueda ejecutar que haga esto sin reiniciar CMD?

Eric Schoonover
fuente
38
En realidad, cada programa que necesita verlos debe reiniciarse. El entorno se copia en la memoria del proceso al inicio y, por lo tanto, ya no tiene conexión alguna con los entornos definidos por el sistema.
Joey
15
después de leer esto, me di cuenta de que no hay cuchara ;) en el mundo real, simplemente reinicia cmd.
n611x007
No es un comando, por lo que no es una respuesta, pero hay soporte para él usando Win32 API si leo lo siguiente correctamente: support.microsoft.com/en-us/help/104011/… Debería poder compilar esa línea en un programa C simple y ejecútelo siguiendo las actualizaciones de las variables de entorno.
Charles Grunwald
WM_SETTINGCHANGE (la api win32 mencionada por @CharlesGrunwald) no funciona para ventanas cmd.exe de acuerdo con este hilo: github.com/chocolatey/choco/issues/1589 - es la razón por la que escribieron el comando refreshenv
davr

Respuestas:

137

Puede capturar las variables de entorno del sistema con un script vbs, pero necesita un script bat para cambiar realmente las variables de entorno actuales, por lo que esta es una solución combinada.

Cree un archivo llamado que resetvars.vbscontenga este código y guárdelo en la ruta:

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

cree otro nombre de archivo resetvars.bat que contenga este código, la misma ubicación:

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

Cuando desee actualizar las variables de entorno, simplemente ejecute resetvars.bat


Apologética :

Los dos problemas principales que tuve con esta solución fueron

a. No pude encontrar una manera sencilla de exportar variables de entorno de un script vbs de nuevo al símbolo del sistema, y

si. La variable de entorno PATH es una concatenación del usuario y las variables PATH del sistema.

No estoy seguro de cuál es la regla general para las variables en conflicto entre el usuario y el sistema, por lo que elegí hacer que el sistema de anulación del usuario, excepto en la variable PATH que se maneja específicamente.

Utilizo el extraño mecanismo vbs + bat + temporal bat para solucionar el problema de exportar variables desde vbs.

Nota : este script no elimina variables.

Esto probablemente se puede mejorar.

ADICIONAL

Si necesita exportar el entorno de una ventana de cmd a otra, use este script (llamémoslo 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

Ejecutar exportvars.vbsen la ventana que desea exportar desde , luego cambiar a la ventana que desea exportar a , y el tipo:

"%TEMP%\resetvars.bat"
itsadok
fuente
2
Tal vez pueda evitar el archivo temporal usando el FOR / F "tokens = 1, *" %% c IN ('resetvars.vbs') DO
build
2
Como dije en mi respuesta "o, agregue manualmente usando SET en el símbolo del sistema existente". que es lo que esto está haciendo efectivamente. Buena respuesta sin embargo.
Kev
2
@itsadok: dado que esta es ahora la respuesta aceptada, debe agregar una breve explicación al principio para poner el guión en contexto. es decir, señalar que no es posible propagar un cambio de entorno a un cmd.exe abierto sin actualizar manualmente como se indicó anteriormente o reiniciando cmd.exe.
Kev
El script maneja el caso de uso de cambiar las variables de entorno globalmente en "Mi PC ... Variables de entorno", pero si se cambia una variable de entorno en un cmd.exe, el script no lo propagará a otro cmd.exe en ejecución, que creo que es Probablemente un escenario común.
Kev
1
@ Keyslinger: Eso no es realmente posible. Cualquier programa generado puede actualizar su propio entorno, pero no el de la instancia de cmd.exe en ejecución. Un archivo por lotes PUEDE actualizar el entorno cmd.exe, ya que se ejecuta dentro de la misma instancia de cmd.exe.
Ben Voigt
112

Esto es lo que usa 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 .
cobarde anónimo
fuente
66
+1 Si tiene instalado Chocolatey, puede ejecutar RefreshEnvpara obtener variables de entorno actualizadas en su sesión actual.
Martin Valgur
2
Este es un software de utilidad increíblemente útil, muchas gracias por compartirlo.
Sabuncu
10
Nota: Chocolatey ha movido repos y la última versión de este script se puede encontrar aquí (con algunas correcciones de errores): github.com/chocolatey/choco/blob/master/src/…
Michael Burr
1
¿debería funcionar esto Powershelltambién? Solo parece funcionar cmd.exepara mí.
craq
1
A mí me funciona en PowerShell, @craq. Ejecutando Windows10 x64.
mazunki
100

En Windows 7/8/10, puede instalar Chocolatey, que tiene un script para este incorporado.

Después de instalar Chocolatey, simplemente escriba refreshenv.

alegre
fuente
esta es una respuesta válida, sería bueno saber de alguien que lo había votado en contra
the_joric
2
¿Qué estoy haciendo mal? $> refreshenv 'refreshenv' no se reconoce como un comando interno o externo, un programa operable o un archivo por lotes.
aclokay
@aclokay No estoy seguro. Proporcione más detalles sobre su sistema conf para depurar. Mientras tanto, puede consultar un tema abierto similar aquí. github.com/chocolatey/choco/issues/250
alegre
A mí tampoco me funciona. Estoy en W7 profesional, tal vez solo funcione en versiones más completas.
Alseether
1
Si refreshenv existe prematuramente su script (como lo hizo para mí), puede usar "llamar a RefreshEnv.cmd" en su lugar. (ver github.com/chocolatey/choco/issues/1461 )
sfiss
59

Por diseño no hay un construido en mecanismo para que Windows propague una variable de entorno agregar / cambiar / eliminar a un cmd.exe que ya se está ejecutando, ya sea desde otro cmd.exe o desde "Mi PC -> Propiedades -> Configuración avanzada -> Variables de entorno".

Si modifica o agrega una nueva variable de entorno fuera del alcance de un símbolo del sistema abierto existente, debe reiniciar el símbolo del sistema o agregar manualmente usando SET en el símbolo del sistema existente.

La última respuesta aceptada muestra una solución parcial al actualizar manualmente todas las variables de entorno en un script. El script maneja el caso de uso de cambiar las variables de entorno globalmente en "Mi PC ... Variables de entorno", pero si se cambia una variable de entorno en un cmd.exe, el script no se propagará a otro cmd.exe en ejecución.

Kev
fuente
Si alguien tiene una solución que funcione para este problema, me registraré periódicamente y podría cambiar la respuesta aceptada.
Eric Schoonover
1
Esta no debería ser la respuesta aceptada simplemente porque no responde la pregunta formulada. esta pregunta debe permanecer sin una respuesta aceptada hasta que se encuentre una.
shoosh
44
Y molestamente, las instancias adicionales de cmd.exe no cuentan. Ellos todos tienen que ser matado antes de que el cambio se refleja en cualquier nuevo cmd.exe de.
66
Los comentarios negativos y la disminución de esta respuesta muestran cuán roto es el desbordamiento de la pila a veces. Kev ha dado la respuesta correcta. El hecho de que no te guste no es motivo para marcarlo.
David Arno
Kev definitivamente responde la pregunta. La pregunta es que no hay una solución integrada.
Eric Schoonover
40

Encontré esta respuesta antes de encontrar una solución más fácil.

Simplemente reinicie explorer.exeen el Administrador de tareas.

No probé, pero es posible que también deba volver a abrir el símbolo del sistema.

Crédito a Timo Huovinen aquí: Nodo no reconocido aunque instalado correctamente (si esto le ayudó, por favor, den crédito de comentario de este hombre).

wharding28
fuente
Llegué aquí porque estaba tratando de agregar una herramienta externa a Visual Studio para poder abrir un símbolo del sistema en la raíz de mi solución, como se describe en este blog: neverindoubtnet.blogspot.com/2012/10/… ... y Tuve problemas similares ... Estaba tratando de que aparezca "git" en mi variable de ruta. Agregué los directorios git a la variable PATH pero luego no aparecerían en el símbolo del sistema que abriría desde Visual Studio. La solución simple era reiniciar Visual Studio. Luego, las nuevas adiciones a la variable PATH fueron visibles en cmd.
David Barrows
44
Esa solución me ayuda en Windows 10
ganchito55
77
La pregunta era: "¿Hay algún comando que pueda ejecutar que haga esto sin reiniciar CMD ?"
Florian F
Ok desde el administrador de tareas no pude reiniciar explorer.exe, solo terminarlo. Lo hice pero mi barra de tareas se ha roto. Para iniciar el explorador; exe es realmente simple. "Ctrl + shift + escape" -> archivo -> "ejecutar nueva tarea" -> "explorer.exe" hizo el trabajo por mí. Y sí, después de todo env var se ha utilizado en la nueva ventana de cmd. Gracias por todo
Oscar
Buena solución, gracias! Para expandir y abordar el comentario de @Oscar: Inicie una cmdventana como Administrador. Usa el comando taskkill /f /im explorer.exe && explorer.exe. Esto matará el proceso explorer.exe y lo reiniciará.
S3DEV
32

Esto funciona en Windows 7: SET PATH=%PATH%;C:\CmdShortcuts

probado escribiendo echo% PATH% y funcionó, bien. también se establece si abre un nuevo cmd, ya no es necesario reiniciar esos molestos :)

kristofer månsson
fuente
1
No funciona para "nuevo cmd" para mí (Win7 x64). Vea el video en pantalla
Igor
26
Esto no resuelve la pregunta que se hace, ni debería hacerlo. La pregunta original es cómo actualizar una variable de entorno a un valor que se ha establecido fuera de ese terminal.
csauve
Aunque esto no responde la pregunta, proporciona la mitad de la mejor solución de trabajo. Utilizo esto, para cualquier variable que esté configurando, luego abro el panel de control y agrego la variable ambiental globalmente. No me gusta usarlo setxporque hereda el entorno actual que puede tener variables que se han modificado y no lo que quiero permanentemente. Hacerlo de esta manera me permite evitar reiniciar la consola para usar las variables, al tiempo que evito el problema de no tenerlas disponibles globalmente en el futuro.
dgo
25

Use "setx" y reinicie el indicador de cmd

Hay una herramienta de línea de comando llamada " setx " para este trabajo. Es para leer y escribir variables env. Las variables persisten después de cerrar la ventana de comandos.

"Crea o modifica variables de entorno en el entorno del usuario o del sistema, sin necesidad de programación o secuencias de comandos. El comando setx también recupera los valores de las claves de registro y los escribe en archivos de texto".

Nota: las variables creadas o modificadas por esta herramienta estarán disponibles en futuras ventanas de comandos pero no en la ventana de comandos actual de CMD.exe. Entonces, tienes que reiniciar.

Si setxfalta:


O modificar el registro

MSDN dice:

Para agregar o modificar mediante programación las variables de entorno del sistema, agréguelas a la clave de registro HKEY_LOCAL_MACHINE \ System \ CurrentControlSet \ Control \ Session Manager \ Environment , luego transmita un mensaje WM_SETTINGCHANGE con lParam configurado en la cadena " Environment ".

Esto permite que aplicaciones, como el shell, recojan sus actualizaciones.

Jens A. Koch
fuente
1
¿Podría ampliar cómo utilizar setx para leer una variable de entorno? He revisado varios documentos y simplemente no los estoy viendo. : - /
Mark Ribau
2
setx VARIABLE -k "HKEY_LOCAL_MACHINE \ Software \ Microsoft \ WindowsNT \ CurrentVersion \ CurrentVersion" echo% VARIABLE%
Jens A. Koch
3
entorno actual del sistema: entorno HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment\VARIABLEactual del usuario: HKEY_CURRENT_USER\Environment\VARIABLE
Mark Ribau
55
setx /? dice en una nota: "En un sistema local, las variables creadas o modificadas por esta herramienta estarán disponibles en futuras ventanas de comandos pero no en la ventana de comandos CMD.exe actual ". OP quería actualizar el cmd actual.
Superole
44
¡El comprador tenga cuidado! Si tiene un tiempo particularmente largo %PATH%, ¡ setxpuede truncar esto a 1024 bytes! Y así, su noche se desvaneció
FaCE
15

Llamar a esta función me ha funcionado:

VOID Win32ForceSettingsChange()
{
    DWORD dwReturnValue;
    ::SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, (LPARAM) "Environment", SMTO_ABORTIFHUNG, 5000, &dwReturnValue);
}
Brian Weed
fuente
8
y no todos los programas escuchan este mensaje (de hecho, la mayoría de ellos probablemente no)
Rehan Khwaja
No, también funciona para programas sin GUI. En cuanto a los programas de escucha ... el problema es garantizar que un programa reiniciado reciba el entorno actualizado, y esto le da eso.
user2023370
11

El mejor método que se me ocurrió fue simplemente hacer una consulta del Registro. Aquí está mi ejemplo.

En mi ejemplo, hice una instalación usando un archivo Batch que agregó nuevas variables de entorno. Tenía que hacer cosas con esto tan pronto como se completara la instalación, pero no pude generar un nuevo proceso con esas nuevas variables. Probé generar otra ventana del explorador y volví a llamar a cmd.exe y funcionó, pero en Vista y Windows 7, Explorer solo se ejecuta como una instancia única y normalmente como la persona que inició sesión. Esto fallará con la automatización ya que necesito mis créditos administrativos para hacer cosas independientemente de la ejecución desde el sistema local o como administrador en la caja. La limitación a esto es que no maneja cosas como la ruta, esto solo funcionó en variables de entorno simples. Esto me permitió usar un lote para ir a un directorio (con espacios) y copiar archivos ejecutados .exes y etc. Esto fue escrito hoy desde los recursos de mayo en stackoverflow.com

Llamadas de lote originales a nuevo lote:

testenvget.cmd SDROOT (o cualquiera que sea la variable)

@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

También hay otro método que se me ocurrió a partir de varias ideas diferentes. Por favor ver más abajo. Básicamente, esto obtendrá la variable de ruta más nueva del registro, sin embargo, esto causará una serie de problemas porque la consulta del registro dará variables en sí misma, lo que significa que en todas partes hay una variable que no funcionará, por lo que para combatir este problema I básicamente doblar el camino. Muy asqueroso. El método más preferido sería hacer: Establecer ruta =% Ruta%; C: \ Archivos de programa \ Software .... \

Independientemente de aquí esté el nuevo archivo por lotes, tenga cuidado.

@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
fuente
8

La forma más fácil de agregar una variable a la ruta sin reiniciar para la sesión actual es abrir el símbolo del sistema y escribir:

PATH=(VARIABLE);%path%

y presione enter.

para verificar si su variable está cargada, escriba

PATH

y presione enter. Sin embargo, la variable solo será una parte de la ruta hasta que reinicie.

Richard Woodruff
fuente
no funcionó para mí, no pude acceder a los binarios en la variable de ruta
user2305193
7

Es posible hacer esto sobrescribiendo la tabla de entorno dentro de un proceso específico.

Como prueba de concepto, escribí esta aplicación de muestra, que acaba de editar una sola variable de entorno (conocida) en un proceso 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;
}

Salida de muestra:

>set ENVTEST=abc

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

>set ENVTEST
ENVTEST=def

Notas

Este enfoque también se limitaría a restricciones de seguridad. Si el objetivo se ejecuta a una elevación más alta o una cuenta más alta (como SYSTEM), entonces no tendríamos permiso para editar su memoria.

Si desea hacer esto en una aplicación de 32 bits, los desplazamientos codificados anteriormente cambiarían a 0x10 y 0x48 respectivamente. Estas compensaciones se pueden encontrar volcando las estructuras _PEB y _RTL_USER_PROCESS_PARAMETERS en un depurador (por ejemplo, en WinDbg dt _PEBy dt _RTL_USER_PROCESS_PARAMETERS)

Para cambiar la prueba de concepto en lo que el OP necesita, solo enumeraría las variables actuales del entorno del sistema y del usuario (como se documenta en la respuesta de @ tsadok) y escribiría la tabla de entorno completa en la memoria del proceso de destino.

Editar: el tamaño del bloque de entorno también se almacena en la estructura _RTL_USER_PROCESS_PARAMETERS, pero la memoria se asigna en el montón del proceso. Entonces, desde un proceso externo no tendríamos la capacidad de cambiar su tamaño y hacerlo más grande. Jugué con el uso de VirtualAllocEx para asignar memoria adicional en el proceso de destino para el almacenamiento del entorno, y pude configurar y leer una tabla completamente nueva. Desafortunadamente, cualquier intento de modificar el entorno de los medios normales se bloqueará y quemará ya que la dirección ya no apunta al montón (se bloqueará en RtlSizeHeap).

josh poley
fuente
6

Las variables de entorno se mantienen en HKEY_LOCAL_MACHINE \ SYSTEM \ ControlSet \ Control \ Session Manager \ Environment.

Muchos de los entornos útiles, como Path, se almacenan como REG_SZ. Hay varias formas de acceder al registro, incluido REGEDIT:

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

La salida comienza con números mágicos. Entonces, para buscarlo con el comando find, debe escribirse y redirigirse:type <filename> | findstr -c:\"Path\"

Entonces, si solo desea actualizar la variable de ruta en su sesión de comando actual con las propiedades del sistema, el siguiente script por lotes funciona bien:

RefreshPath.cmd:

    @echo off

    REM Esta solución solicita elevación para poder leer desde el registro.

    si existe% temp% \ env.reg del% temp% \ env.reg / q / f

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

    si no existe% temp% \ env.reg (
       echo "No se puede escribir el registro en la ubicación temporal"
       salida 1
       )

    SETLOCAL EnableDelayedExpansion

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

     ENDLOCAL

     para / f "tokens = *" %% i en (% temp% \ newpath) establecer ruta = %% i
Algonauta
fuente
55
Las variables de entorno no se guardan en el registro. Lo que se mantiene en el registro es una plantilla , a partir de la cual los programas como Windows Explorer (re) construyen sus variables de entorno cuando se les notifica que lo hagan . Las variables de entorno reales son por proceso y se almacenan en el propio espacio de direcciones de cada proceso, inicialmente heredado de su proceso padre y modificable a partir de entonces según el capricho del proceso.
JdeBP
5

Intente abrir un nuevo símbolo del sistema como administrador. Esto funcionó para mí en Windows 10. (Sé que esta es una respuesta anterior, pero tuve que compartirla porque tener que escribir un script VBS solo por esto es absurdo).

estebro
fuente
5

El reinicio del explorador lo hizo por mí, pero solo para los nuevos terminales cmd.

El terminal en el que configuré la ruta ya podía ver la nueva variable Ruta (en Windows 7).

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

Lo confuso podría ser que hay algunos lugares para comenzar el cmd. En mi caso, ejecuté cmd desde el explorador de Windows y las variables de entorno no cambiaron, mientras que al iniciar cmd desde "ejecutar" (tecla de Windows + r) las variables de entorno cambiaron .

En mi caso, solo tuve que eliminar el proceso del explorador de Windows desde el administrador de tareas y luego reiniciarlo nuevamente desde el administrador de tareas .

Una vez que hice esto, tuve acceso a la nueva variable de entorno desde un cmd que se generó desde el explorador de Windows.

Daniel Fensterheim
fuente
3

Uso el siguiente código en mis scripts por lotes:

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

Al usar SET después de SETX , es posible usar la variable "local" directamente sin reiniciar la ventana de comandos. Y en la próxima ejecución, se usará la variable de entorno.

Sebastian
fuente
Mientras obtengo lo que has hecho, lo más probable es que quiera algo para guiones paralelos, un guión establece globales mientras que otro los lee. De lo contrario, no tiene sentido involucrar a setx, el conjunto sería suficiente.
JasonXA
3

Me gustó el enfoque seguido de chocolate, según lo publicado en la respuesta anónima de cobarde, ya que es un enfoque puramente discontinuo. Sin embargo, deja un archivo temporal y algunas variables temporales por ahí. Hice una versión más limpia para mí.

Haga un archivo en refreshEnv.batalgún lugar de su PATH. Actualice su entorno de consola mediante la ejecución 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
fuente
¿Es esto también para% CLIENTNAME%? - no funcionó para mí - stackoverflow.com/questions/37550160/…
Igor L.
% CLIENTNAME% no está disponible en mi entorno, y al leer su pregunta, asumiré que es algo establecido por un proceso externo. (Cuando un proceso inicia un proceso hijo, puede ajustar el entorno para ese hijo). Dado que no forma parte de las variables de entorno reales, este script no lo actualizará.
DieterDP
Hola @DieterDP, ¡tu solución funciona para mí! Estoy usando Windows 10 en una máquina de 64 bits. Aparece un error: "ERROR: el sistema no pudo encontrar la clave o valor de registro especificado". Sin embargo, la actualización de las variables de entorno es exitosa. ¿De dónde viene el error?
K.Mulier
Es difícil de decir sin probarlo yo mismo, pero supongo que la estructura del registro en W10 puede diferir ligeramente. Si lo desea, intente buscar el error ejecutando los comandos en la línea de comandos.
DieterDP
2

Si se trata solo de uno (o algunos) vars específicos que desea cambiar, creo que la forma más fácil es una solución alternativa : simplemente configúrelo en su entorno Y en su sesión de consola actual

  • Set pondrá el var en tu sesión actual
  • SetX pondrá la var en el entorno, pero NO en su sesión actual

Tengo este script por lotes simple para cambiar mi Maven de Java7 a Java8 (que son ambos env. Vars) La carpeta por lotes está en mi PATH var, por lo que siempre puedo llamar ' j8 ' y dentro de mi consola y en el entorno mi JAVA_HOME var se cambia:

j8.bat:

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

Hasta ahora encuentro que esto funciona mejor y más fácil. Probablemente desee que esto esté en un comando, pero simplemente no está allí en Windows ...

Jeroen van Dijk-Jun
fuente
2

La solución que he estado usando durante algunos años:

@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

Editar: Woops, aquí está la versión actualizada.

Charles Grunwald
fuente
Me gusta tu respuesta. Publicar la misma respuesta a mi pregunta aquí stackoverflow.com/q/61473551/1082063 y voy a aceptarlo como la respuesta. Gracias.
David I. McIntosh
1

No hay un camino directo, como dijo Kev. En la mayoría de los casos, es más sencillo generar otro cuadro CMD. Más molesto, los programas en ejecución tampoco están al tanto de los cambios (aunque IIRC podría haber un mensaje de transmisión para ver y ser notificado de dicho cambio).

Ha sido peor: en versiones anteriores de Windows, tenía que cerrar la sesión y volver a iniciarla para tener en cuenta los cambios ...

PhiLho
fuente
1

Utilizo este script de Powershell para agregar a la variable PATH . Creo que con un pequeño ajuste también puede funcionar en su caso.

#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
fuente
1

Gracias por publicar esta pregunta que es bastante interesante, incluso en 2019 (de hecho, no es fácil renovar el cmd de shell ya que es una instancia única como se mencionó anteriormente), porque renovar las variables de entorno en Windows permite realizar muchas tareas de automatización sin tener que reiniciar manualmente la línea de comando.

Por ejemplo, usamos esto para permitir que el software se implemente y configure en una gran cantidad de máquinas que reinstalamos regularmente. Y debo admitir que tener que reiniciar la línea de comandos durante la implementación de nuestro software sería muy poco práctico y requeriría que encontremos soluciones que no sean necesariamente agradables. Vayamos a nuestro problema. Procedemos de la siguiente manera.

1 - Tenemos un script por lotes que a su vez llama un script de PowerShell como este

[archivo: task.cmd] .

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

2 - Después de esto, el script refresh.ps1 renueva las variables de entorno utilizando claves de registro (GetValueNames (), etc.). Luego, en el mismo script de PowerShell, solo tenemos que llamar a las nuevas variables de entorno disponibles. Por ejemplo, en un caso típico, si acabamos de instalar nodeJS antes con cmd usando comandos silenciosos, después de llamar a la función, podemos llamar directamente a npm para instalar, en la misma sesión, paquetes particulares como los siguientes.

[archivo: 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 vez que finaliza el script de PowerShell, el script de cmd continúa con otras tareas. Ahora, una cosa a tener en cuenta es que después de completar la tarea, cmd todavía no tiene acceso a las nuevas variables de entorno, incluso si el script de powershell las ha actualizado en su propia sesión. Es por eso que hacemos todas las tareas necesarias en el script de PowerShell que puede llamar a los mismos comandos que cmd, por supuesto.

Andy McRae
fuente
0

Editar: esto solo funciona si los cambios en el entorno que está haciendo son el resultado de ejecutar un archivo por lotes.

Si un archivo por lotes comienza con SETLOCAL, siempre se desenredará en su entorno original al salir, incluso si olvida llamarENDLOCAL antes de que el lote salga, o si aborta inesperadamente.

Casi todos los archivos por lotes que escribo comienzan SETLOCALya que en la mayoría de los casos no quiero que los efectos secundarios de los cambios en el entorno permanezcan. En los casos en que quiero que ciertos cambios de variables de entorno se propaguen fuera del archivo por lotes, mi último se ENDLOCALve así:

ENDLOCAL & (
  SET RESULT1=%RESULT1%
  SET RESULT2=%RESULT2%
)
pupilos
fuente
-1

Para resolver esto, he cambiado la variable de entorno usando AMBOS setx y set, y luego reinicié todas las instancias de explorer.exe. De esta forma, cualquier proceso iniciado posteriormente tendrá la nueva variable de entorno.

Mi secuencia de comandos por lotes para hacer esto:

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

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

El problema con este enfoque es que todas las ventanas del explorador que están abiertas actualmente se cerrarán, lo que probablemente sea una mala idea. Pero vea la publicación de Kev para saber por qué esto es necesario

Jens Hykkelbjerg
fuente