Se eu modificar ou adicionar uma variável de ambiente, tenho que reiniciar o prompt de comando. Existe um comando que eu poderia executar que faria isso sem reiniciar o CMD?
Se eu modificar ou adicionar uma variável de ambiente, tenho que reiniciar o prompt de comando. Existe um comando que eu poderia executar que faria isso sem reiniciar o CMD?
Respostas:
Você pode capturar as variáveis de ambiente do sistema com um script vbs, mas é necessário um script bat para alterar as variáveis de ambiente atuais, portanto, essa é uma solução combinada.
Crie um arquivo chamado resetvars.vbs
contendo esse código e salve-o no caminho:
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
crie outro nome de arquivo resetvars.bat contendo este código, no mesmo local:
@echo off
%~dp0resetvars.vbs
call "%TEMP%\resetvars.bat"
Quando você deseja atualizar as variáveis de ambiente, basta executar resetvars.bat
Apologética :
Os dois principais problemas que tive com esta solução foram:
uma. Não consegui encontrar uma maneira direta de exportar variáveis de ambiente de um script vbs de volta para o prompt de comando e
b. a variável de ambiente PATH é uma concatenação do usuário e das variáveis PATH do sistema.
Não tenho certeza de qual é a regra geral para variáveis conflitantes entre usuário e sistema, portanto optei por substituir o sistema pelo usuário, exceto na variável PATH, que é tratada especificamente.
Eu uso o estranho mecanismo vbs + bat + bat temporário para solucionar o problema de exportar variáveis do vbs.
Nota : este script não exclui variáveis.
Provavelmente isso pode ser melhorado.
ADICIONADO
Se você precisar exportar o ambiente de uma janela de cmd para outra, use este script (vamos chamá-lo 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
Execute exportvars.vbs
na janela que pretende exportar a partir de , em seguida, mudar para a janela que você deseja exportar para , e digite:
"%TEMP%\resetvars.bat"
Aqui está o que Chocolatey usa.
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 obter variáveis de ambiente atualizadas na sua sessão atual.
Powershell
também? Parece apenas funcionar cmd.exe
para mim.
No Windows 7/8/10, você pode instalar o Chocolatey, que possui um script para este built-in.
Depois de instalar o Chocolatey, basta digitar refreshenv
.
Pelo projeto não há um construído em mecanismo para Windows para propagar um ambiente variável add / Alterar / remover para um cmd.exe já em execução, ou de outro cmd.exe ou a partir de "Meu Computador -> Propriedades -> Configurações avançadas -> Variáveis ambientais".
Se você modificar ou adicionar uma nova variável de ambiente fora do escopo de um prompt de comando aberto existente, será necessário reiniciar o prompt de comando ou adicionar manualmente usando SET no prompt de comando existente.
A resposta aceita mais recente mostra uma solução alternativa, atualizando manualmente todas as variáveis de ambiente em um script. O script lida com o caso de uso de alterar variáveis de ambiente globalmente em "Meu Computador ... Variáveis de Ambiente", mas se uma variável de ambiente for alterada em um cmd.exe, o script não a propagará para outra cmd.exe em execução.
Me deparei com essa resposta antes de finalmente encontrar uma solução mais fácil.
Basta reiniciar explorer.exe
no Gerenciador de tarefas.
Não testei, mas também pode ser necessário reabrir o prompt de comando.
Crédito para Timo Huovinen aqui: Nó não reconhecido, embora instalado com êxito (se isso ajudou você, dê o crédito de comentário deste homem).
cmd
janela como Administrador. Use o comando taskkill /f /im explorer.exe && explorer.exe
. Isso matará o processo explorer.exe e o reiniciará.
Isso funciona no Windows 7: SET PATH=%PATH%;C:\CmdShortcuts
testado digitando echo% PATH% e funcionou, tudo bem. Também defina se você abrir um novo cmd, não haverá mais necessidade de reinicializações traquinas :)
setx
porque herda o ambiente atual, que pode ter variáveis modificadas e não o que eu quero permanentemente. Fazer dessa maneira me permite evitar reiniciar o console para usar as variáveis, evitando o problema de não tê-las disponíveis globalmente no futuro.
Use "setx" e reinicie o prompt do cmd
Existe uma ferramenta de linha de comando denominada " setx " para este trabalho. É para ler e escrever variáveis env. As variáveis persistem após a janela de comando ser fechada.
"Cria ou modifica variáveis de ambiente no ambiente do usuário ou do sistema, sem a necessidade de programação ou script. O comando setx também recupera os valores das chaves do Registro e os grava em arquivos de texto".
Nota: as variáveis criadas ou modificadas por esta ferramenta estarão disponíveis em janelas de comando futuras, mas não na janela de comando atual do CMD.exe. Então, você precisa reiniciar.
Se setx
estiver faltando:
Ou modifique o registro
MSDN diz:
Para adicionar ou modificar programaticamente variáveis de ambiente do sistema, adicione-as à chave de registro HKEY_LOCAL_MACHINE \ System \ CurrentControlSet \ Control \ Session Manager \ Environment e depois transmita uma mensagem WM_SETTINGCHANGE com lParam definido como a cadeia " Environment ".
Isso permite que aplicativos, como o shell, obtenham suas atualizações.
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment\VARIABLE
atual do usuário: HKEY_CURRENT_USER\Environment\VARIABLE
%PATH%
, setx
pode truncar isso para 1024 bytes! E assim, sua noite desapareceu
Chamar essa função funcionou para mim:
VOID Win32ForceSettingsChange()
{
DWORD dwReturnValue;
::SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, (LPARAM) "Environment", SMTO_ABORTIFHUNG, 5000, &dwReturnValue);
}
O melhor método que surgiu foi apenas fazer uma consulta no Registro. Aqui está o meu exemplo.
No meu exemplo, eu fiz uma instalação usando um arquivo em lotes que adicionou novas variáveis de ambiente. Eu precisava fazer isso assim que a instalação fosse concluída, mas não consegui gerar um novo processo com essas novas variáveis. Eu testei gerando outra janela do explorador e liguei novamente para o cmd.exe e isso funcionou, mas no Vista e no Windows 7, o Explorer é executado apenas como uma instância única e normalmente como a pessoa conectada. faça as coisas independentemente da execução no sistema local ou como administrador na caixa. A limitação disso é que ele não lida com coisas como path, isso só funcionou em variáveis de ambiente simples. Isso me permitiu usar um lote para ir para um diretório (com espaços) e copiar arquivos executados .exes e etc. Isso foi escrito hoje a partir de maio de recursos em stackoverflow.com
Lote original chama para novo lote:
testenvget.cmd SDROOT (ou qualquer que seja a variável)
@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
Também existe outro método que surgiu de várias idéias diferentes. Por favor veja abaixo. Isso basicamente obterá a variável de caminho mais recente do registro, no entanto, isso causará vários problemas, pois a consulta do registro fornecerá variáveis por si só, o que significa que em todos os lugares há uma variável que não funcionará, portanto, para combater esse problema, basicamente duplicar o caminho. Muito nojento. O método mais preferido seria: Definir caminho =% Path%; C: \ Arquivos de programas \ Software .... \
Independentemente do novo arquivo em lotes, tenha 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
A maneira mais fácil de adicionar uma variável ao caminho sem reiniciar a sessão atual é abrir o prompt de comando e digite:
PATH=(VARIABLE);%path%
e pressione enter.
para verificar se sua variável foi carregada, digite
PATH
e pressione enter. No entanto, a variável fará parte apenas do caminho até a reinicialização.
É possível fazer isso substituindo a Tabela de ambiente dentro de um processo especificado.
Como prova de conceito, escrevi este aplicativo de exemplo, que acabou de editar uma única variável de ambiente (conhecida) em um 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;
}
Saída de amostra:
>set ENVTEST=abc
>cppTest.exe 13796
Target PID: 13796
PEB: 000007FFFFFD3000
UserProcessParameters: 00000000004B2F30
environment: 000000000052E700
Offset: 1528
>set ENVTEST
ENVTEST=def
Essa abordagem também seria limitada a restrições de segurança. Se o destino for executado em uma elevação mais alta ou em uma conta mais alta (como SYSTEM), não teremos permissão para editar sua memória.
Se você quisesse fazer isso em um aplicativo de 32 bits, as compensações codificadas anteriormente mudariam para 0x10 e 0x48, respectivamente. Essas compensações podem ser encontradas despejando as estruturas _PEB e _RTL_USER_PROCESS_PARAMETERS em um depurador (por exemplo, no WinDbg dt _PEB
e dt _RTL_USER_PROCESS_PARAMETERS
)
Para alterar a prova de conceito para o que o OP precisa, ele apenas enumera as variáveis atuais do sistema e do ambiente do usuário (como documentadas pela resposta do @ tsadok) e grava a tabela de ambiente inteira na memória do processo de destino.
Editar: o tamanho do bloco de ambiente também é armazenado na estrutura _RTL_USER_PROCESS_PARAMETERS, mas a memória é alocada no heap do processo. Portanto, a partir de um processo externo, não teríamos a capacidade de redimensioná-lo e torná-lo maior. Eu brinquei com o uso do VirtualAllocEx para alocar memória adicional no processo de destino para o armazenamento do ambiente e pude definir e ler uma tabela totalmente nova. Infelizmente, qualquer tentativa de modificar o ambiente de maneira normal falha e queima quando o endereço não aponta mais para o heap (ele trava no RtlSizeHeap).
As variáveis de ambiente são mantidas em HKEY_LOCAL_MACHINE \ SYSTEM \ ControlSet \ Control \ Session Manager \ Environment.
Muitos dos ambientes úteis, como Path, são armazenados como REG_SZ. Existem várias maneiras de acessar o registro, incluindo REGEDIT:
REGEDIT /E <filename> "HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Session Manager\Environment"
A saída começa com números mágicos. Portanto, para pesquisar com o comando find, ele precisa ser digitado e redirecionado:type <filename> | findstr -c:\"Path\"
Portanto, se você deseja apenas atualizar a variável de caminho na sua sessão de comando atual com as propriedades do sistema, o seguinte script em lote funciona bem:
RefreshPath.cmd:
@echo off REM Esta solução solicita elevação para ler no registro. se existir% temp% \ env.reg del% temp% \ env.reg / q / f REGEDIT / E% temp% \ env.reg "HKEY_LOCAL_MACHINE \ SYSTEM \ ControlSet001 \ Control \ Gerenciador de sessões \ Ambiente" se não existir% temp% \ env.reg ( echo "Não foi possível gravar o registro no local temporário" saída 1 ) SETLOCAL EnableDelayedExpansion para / f "tokens = 1,2 * delimita ==" %% i em ('digite% temp% \ env.reg ^ | findstr -c: \ "Caminho \" =') do ( setathath = %% ~ j echo! upath: \\ = \! >% temp% \ caminho novo ) ENDLOCAL para / f "tokens = *" %% i em (% temp% \ caminho novo) define o caminho = %% i
A coisa confusa pode ser que existem alguns lugares para começar o cmd. No meu caso, eu executei o cmd no windows explorer e as variáveis de ambiente não mudaram, enquanto ao iniciar o cmd a partir da "execução" (tecla windows + r) as variáveis de ambiente foram alteradas .
No meu caso, eu apenas tive que matar o processo do Windows Explorer do gerenciador de tarefas e depois reiniciá-lo novamente a partir do gerenciador de tarefas .
Depois disso, tive acesso à nova variável de ambiente a partir de um cmd gerado no Windows Explorer.
Eu uso o seguinte código nos meus scripts em lote:
if not defined MY_ENV_VAR (
setx MY_ENV_VAR "VALUE" > nul
set MY_ENV_VAR=VALUE
)
echo %MY_ENV_VAR%
Usando SET após SETX , é possível usar a variável "local" diretamente, sem reiniciar a janela de comando. E na próxima execução, a variável de ambiente será usada.
Gostei da abordagem seguida por chocolatey, como publicado na resposta do covarde anônimo, já que é uma abordagem em lote puro. No entanto, ele deixa um arquivo temporário e algumas variáveis temporárias por aí. Eu fiz uma versão mais limpa para mim.
Faça um arquivo refreshEnv.bat
em algum lugar no seu PATH
. Atualize o ambiente do console executando 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.
Se se trata de apenas um (ou alguns) vars específicos que você deseja alterar, acho que a maneira mais fácil é uma solução alternativa : basta definir no seu ambiente E na sua sessão atual do console
Eu tenho esse script em lote simples para alterar meu Maven de Java7 para Java8 (que são ambos env. Vars). A pasta em lote está na minha PATH var, para que eu possa sempre chamar ' j8 ' e no meu console e no ambiente minha JAVA_HOME var é alterado:
j8.bat:
@echo off
set JAVA_HOME=%JAVA_HOME_8%
setx JAVA_HOME "%JAVA_HOME_8%"
Até agora, acho isso funcionando melhor e mais fácil. Você provavelmente deseja que este seja um comando, mas simplesmente não existe no Windows ...
A solução que uso há alguns anos:
@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
Edit: Woops, aqui está a versão atualizada.
Não existe um caminho reto, como disse Kev. Na maioria dos casos, é mais simples gerar outra caixa CMD. Mais irritantemente, os programas em execução também não estão cientes das mudanças (embora o IIRC possa haver uma mensagem de transmissão a ser observada para ser notificado sobre essa mudança).
Foi pior: nas versões mais antigas do Windows, era necessário fazer logoff e logon novamente para levar em conta as alterações ...
Eu uso esse script do Powershell para adicionar à variável PATH . Com um pequeno ajuste, também pode funcionar no seu 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%"
Obrigado por postar esta pergunta que é bastante interessante, mesmo em 2019 (De fato, não é fácil renovar o cmd do shell, pois é uma instância única, como mencionado acima), porque a renovação de variáveis de ambiente no Windows permite realizar muitas tarefas de automação sem tendo que reiniciar manualmente a linha de comando.
Por exemplo, usamos isso para permitir que o software seja implantado e configurado em um grande número de máquinas que reinstalamos regularmente. E devo admitir que ter que reiniciar a linha de comando durante a implantação de nosso software seria muito impraticável e exigiria que encontrássemos soluções alternativas que não sejam necessariamente agradáveis. Vamos ao nosso problema. Nós procedemos da seguinte maneira.
1 - Temos um script em lote que, por sua vez, chama um script de PowerShell como este
[arquivo: task.cmd] .
cmd > powershell.exe -executionpolicy unrestricted -File C:\path_here\refresh.ps1
2 - Depois disso, o script refresh.ps1 renova as variáveis de ambiente usando chaves do Registro (GetValueNames (), etc.). Então, no mesmo script do PowerShell, basta chamar as novas variáveis de ambiente disponíveis. Por exemplo, em um caso típico, se acabamos de instalar o nodeJS antes com o cmd usando comandos silenciosos, depois que a função foi chamada, podemos chamar diretamente o npm para instalar, na mesma sessão, pacotes específicos como a seguir.
[arquivo: 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
Depois que o script do PowerShell termina, o script cmd continua com outras tarefas. Agora, lembre-se de que, após a conclusão da tarefa, o cmd ainda não terá acesso às novas variáveis de ambiente, mesmo que o script do PowerShell tenha atualizado as da sua própria sessão. É por isso que fazemos todas as tarefas necessárias no script do PowerShell, que podem chamar os mesmos comandos que o cmd, é claro.
Editar: isso só funciona se as alterações no ambiente que você está fazendo resultarem da execução de um arquivo em lotes.
Se um arquivo em lote começar SETLOCAL
, ele sempre retornará ao seu ambiente original na saída, mesmo se você esquecer de ligar ENDLOCAL
antes da saída do lote ou se for interrompido inesperadamente.
Quase todos os arquivos em lotes que escrevo começam, SETLOCAL
pois, na maioria dos casos, não quero que os efeitos colaterais das alterações no ambiente permaneçam. Nos casos em que eu quero que certas alterações de variáveis de ambiente sejam propagadas para fora do arquivo em lotes, minha última ENDLOCAL
aparência será assim:
ENDLOCAL & (
SET RESULT1=%RESULT1%
SET RESULT2=%RESULT2%
)
Para resolver isso, alterei a variável de ambiente usando BOTH setx e set e reiniciei todas as instâncias do explorer.exe. Dessa forma, qualquer processo iniciado posteriormente terá a nova variável de ambiente.
Meu script em lote para fazer isso:
setx /M ENVVAR "NEWVALUE"
set ENVVAR="NEWVALUE"
taskkill /f /IM explorer.exe
start explorer.exe >nul
exit
O problema dessa abordagem é que todas as janelas do explorer que estão abertas no momento serão fechadas, o que provavelmente é uma má idéia - Mas veja o post de Kev para saber por que isso é necessário.