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?
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?
Respuestas:
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.vbs
contenga 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.vbs
en la ventana que desea exportar desde , luego cambiar a la ventana que desea exportar a , y el tipo:
"%TEMP%\resetvars.bat"
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 .
RefreshEnv
para obtener variables de entorno actualizadas en su sesión actual.
Powershell
también? Solo parece funcionar cmd.exe
para mí.
En Windows 7/8/10, puede instalar Chocolatey, que tiene un script para este incorporado.
Después de instalar Chocolatey, simplemente escriba refreshenv
.
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.
Encontré esta respuesta antes de encontrar una solución más fácil.
Simplemente reinicie explorer.exe
en 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).
cmd
ventana como Administrador. Usa el comando taskkill /f /im explorer.exe && explorer.exe
. Esto matará el proceso explorer.exe y lo reiniciará.
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 :)
setx
porque 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.
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 setx
falta:
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.
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment\VARIABLE
actual del usuario: HKEY_CURRENT_USER\Environment\VARIABLE
%PATH%
, ¡ setx
puede truncar esto a 1024 bytes! Y así, su noche se desvaneció
Llamar a esta función me ha funcionado:
VOID Win32ForceSettingsChange()
{
DWORD dwReturnValue;
::SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, (LPARAM) "Environment", SMTO_ABORTIFHUNG, 5000, &dwReturnValue);
}
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
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.
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
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 _PEB
y 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).
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 <filename> "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
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.
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.
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.bat
algú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.
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
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 ...
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.
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 ...
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%"
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.
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 SETLOCAL
ya 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 ENDLOCAL
ve así:
ENDLOCAL & (
SET RESULT1=%RESULT1%
SET RESULT2=%RESULT2%
)
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