Existe um comando para atualizar variáveis ​​de ambiente no prompt de comando no Windows?

480

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?

Eric Schoonover
fonte
38
Na verdade, todo programa que precisa vê-los precisa ser reiniciado. O ambiente é copiado na memória do processo na inicialização e, portanto, não tem mais nenhuma conexão com os envios definidos pelo sistema.
5119 Joey
15
depois de ler isso, percebi que não há colher ;) no mundo real, você apenas reinicia o cmd.
N611x007
Não é um comando, portanto não é uma resposta, mas há suporte para ele usando a API do Win32 se eu ler o seguinte corretamente: support.microsoft.com/en-us/help/104011/… O Shoud poderá compilar essa linha em um programa C simples e execute-o seguindo atualizações de variáveis ​​de ambiente.
Charles Grunwald
WM_SETTINGCHANGE (a API do win32 mencionada por @CharlesGrunwald) não funciona nas janelas do cmd.exe de acordo com este segmento: github.com/chocolatey/choco/issues/1589 - é a razão pela qual eles escreveram o comando refreshenv
davr

Respostas:

137

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.vbscontendo 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.vbsna janela que pretende exportar a partir de , em seguida, mudar para a janela que você deseja exportar para , e digite:

"%TEMP%\resetvars.bat"
itsadok
fonte
2
Talvez você possa evitar o arquivo temporário usando os tokens FOR / F "= 1, *" %% c IN ('resetvars.vbs') construção DO
tzot
2
Como eu disse na minha resposta "ou, adicione manualmente usando SET no prompt de comando existente". que é o que isso está efetivamente fazendo. Boa resposta embora.
Kev
2
@itsadok - como agora é a resposta aceita, você deve adicionar uma breve explicação no início para colocar o script em contexto. ou seja, ressalte que não é possível propagar uma alteração env var para um cmd.exe aberto sem atualizar manualmente como acima ou reiniciar o cmd.exe.
Kev
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, que eu acho que é provavelmente um cenário comum.
Kev
1
@ Keyslinger: Na verdade, isso não é possível. Qualquer programa gerado pode atualizar seu próprio ambiente, mas não o da instância em execução do cmd.exe. Um arquivo em lotes PODE atualizar o ambiente cmd.exe, porque é executado na mesma instância do cmd.exe.
Ben Voigt
112

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 .
covarde anônimo
fonte
65
+1 Se você tiver o Chocolatey instalado, basta executar RefreshEnvpara obter variáveis ​​de ambiente atualizadas na sua sessão atual.
Martin Valgur 11/09/16
2
Este é um software incrivelmente útil, muito obrigado por compartilhar.
Sabuncu 6/01/19
10
Nota: Chocolatey tem repos movido e a versão mais recente deste script pode ser encontrada aqui (com algumas correções de bugs): github.com/chocolatey/choco/blob/master/src/...
Michael Burr
1
isso deve funcionar Powershelltambém? Parece apenas funcionar cmd.exepara mim.
craq
1
Funciona para mim no PowerShell, @craq. Executando o Windows 10 x64.
mazunki
100

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.

alegre
fonte
esta é a resposta válida, seria bom ouvir de cara que tinha downvoted-lo
the_joric
2
O que estou fazendo errado? $> refreshenv 'refreshenv' não é reconhecido como um comando interno ou externo, programa operável ou arquivo em lote.
aclokay
@aclokay Não tenho certeza. Forneça mais detalhes sobre o seu sistema conf para depuração. Enquanto isso, você pode consultar uma questão em aberto semelhante aqui. github.com/chocolatey/choco/issues/250
jolly
Também não funciona para mim. Estou no W7 professional, talvez ele só funcione em versões mais completas.
alseether
1
Se refreshenv existir prematuramente seu script (como aconteceu comigo), você poderá usar "call RefreshEnv.cmd". (consulte github.com/chocolatey/choco/issues/1461 )
sfiss
59

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.

Kev
fonte
Se alguém tiver uma solução que funcione para esse problema, eu periodicamente faço check-in e posso alterar a resposta aceita.
Eric Schoonover
1
Esta não deve ser a resposta aceita simplesmente porque não responde à pergunta. essa pergunta deve permanecer sem uma resposta aceita até que uma seja encontrada.
shoosh 5/10/08
4
E irritantemente, instâncias extras do cmd.exe não contam. Todos eles precisam ser eliminados antes que a alteração seja refletida em qualquer novo cmd.exe.
6
Os comentários negativos e a marcação para baixo desta resposta mostram como o estouro de pilha quebrada às vezes é. Kev deu a resposta correta. Só porque você não gosta, não há razão para anotá-la.
David Arno
Kev definitivamente responde à pergunta. A questão é que não há solução embutida.
Eric Schoonover
40

Me deparei com essa resposta antes de finalmente encontrar uma solução mais fácil.

Basta reiniciar explorer.exeno 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).

wharding28
fonte
Cheguei aqui porque estava tentando adicionar uma ferramenta externa ao visual studio para abrir um prompt de comando na raiz da minha solução, conforme descrito neste blog: neverindoubtnet.blogspot.com/2012/10/… ... e Eu tive problemas semelhantes ... Eu estava tentando fazer com que "git" apareça na minha variável de caminho. Adicionei os diretórios git à variável PATH, mas eles não apareceriam no prompt de comando que eu abriria no Visual Studio. A solução simples foi reiniciar o Visual Studio. Em seguida, as novas adições à variável PATH eram visíveis em cmd.
David Barrows
4
Essa solução me ajuda no Windows 10
ganchito55 1/16/16
7
A pergunta era: "Existe um comando que eu poderia executar que faria isso sem reiniciar o CMD ?"
Florian F
Ok, no gerenciador de tarefas, não fui capaz de reiniciar o explorer.exe, apenas finalize-o. Eu fiz isso, mas minha barra de tarefas foi quebrada. Para iniciar o explorer; exe é realmente simples. Vamos "Ctrl + shift + escape" -> arquivo -> "executar nova tarefa" -> "explorer.exe" fez o trabalho para mim. E sim, depois que todo o env var foi usado na nova janela do cmd. Obrigado a todos
Oscar
Boa solução, obrigado! Para expandir e abordar o comentário de @Oscar: Inicie uma cmdjanela como Administrador. Use o comando taskkill /f /im explorer.exe && explorer.exe. Isso matará o processo explorer.exe e o reiniciará.
S3DEV
32

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 :)

kristofer månsson
fonte
1
Não está funcionando para "novo cmd" para mim (Win7 x64). Veja screenvideo
Igor
26
Isso não resolve a pergunta que está sendo feita, nem deveria. A questão original é como atualizar uma variável de ambiente para um valor definido fora desse terminal.
csauve
Embora isso não responda à pergunta, ele fornece metade da melhor solução de trabalho. Eu uso isso - para qualquer variável que eu esteja configurando - então abro o painel de controle e adiciono a variável ambiental globalmente. Não gosto de usar setxporque 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.
Dgo 13/05
25

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 setxestiver 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.

Jens A. Koch
fonte
1
Você poderia expandir como usar o setx para ler uma variável de ambiente? Eu já li várias documentações e simplesmente não as vejo. : - /
Mark Ribau
2
-k VARIÁVEL setx "HKEY_LOCAL_MACHINE \ Software \ Microsoft \ WindowsNT \ CurrentVersion \ CurrentVersion" echo% VARIABLE%
Jens A. Koch
3
ambiente atual do sistema: ambiente HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment\VARIABLEatual do usuário: HKEY_CURRENT_USER\Environment\VARIABLE
Mark Ribau 5/11
5
setx /? diz em uma nota: "Em um sistema local, as variáveis ​​criadas ou modificadas por esta ferramenta estarão disponíveis em futuras janelas de comando, mas não na atual janela de comando do CMD.exe." O OP queria atualizar o cmd atual.
Superole 7/10
4
Comprador, cuidado! Se você tiver um tempo particularmente longo %PATH%, setxpode truncar isso para 1024 bytes! E assim, sua noite desapareceu
FaCE 15/08/16
15

Chamar essa função funcionou para mim:

VOID Win32ForceSettingsChange()
{
    DWORD dwReturnValue;
    ::SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, (LPARAM) "Environment", SMTO_ABORTIFHUNG, 5000, &dwReturnValue);
}
Brian Weed
fonte
8
e nem todos os programas de ouvir esta mensagem (na verdade a maioria deles provavelmente não)
Rehan Khwaja
Não, ele também funciona para programas que não são da GUI. Quanto aos programas de escuta ... o problema é garantir que um programa reiniciado receba o ambiente atualizado, e isso oferece a você.
user2023370
11

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
Christopher Holmes
fonte
8

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.

Richard Woodruff
fonte
não funcionou para mim, não pôde acessar os binários na variável de caminho
user2305193 03/01
7

É 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

Notas

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 _PEBe 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).

Josh Poley
fonte
6

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 &lt;filename&gt; "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
Algonaut
fonte
5
Variáveis ​​de ambiente não são mantidas no registro. O que é mantido no registro é um modelo , a partir do qual programas como o Windows Explorer (re) constroem suas variáveis ​​de ambiente quando notificados . As variáveis ​​de ambiente reais são por processo e são armazenadas no próprio espaço de endereço de cada processo, herdado inicialmente do processo pai e modificável a partir de então por capricho do processo.
JdeBP
5

Tente abrir um novo prompt de comando como administrador. Isso funcionou para mim no Windows 10. (Eu sei que essa é uma resposta antiga, mas tive que compartilhar isso porque ter que escrever um script VBS apenas por isso é um absurdo).

estebro
fonte
5

A reinicialização do explorer fez isso por mim, mas apenas para novos terminais cmd.

O terminal que eu defini o caminho já pode ver a nova variável Path (no Windows 7).

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

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.

Daniel Fensterheim
fonte
3

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.

Sebastian
fonte
Enquanto eu entendo o que você fez, provavelmente ele quer algo para scripts paralelos, um script define globais enquanto outro os lê. Caso contrário, não há sentido em envolver setx, set seria suficiente.
JasonXA
3

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.batem 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.
DieterDP
fonte
isso também é para% CLIENTNAME%? - não funcionou para mim - stackoverflow.com/questions/37550160/…
Igor L.
O% CLIENTNAME% não está disponível no meu ambiente e, ao ler sua pergunta, vou assumir que é algo definido por um processo externo. (Quando um processo inicia um processo filho, ele pode ajustar o ambiente para esse filho.) Como não faz parte das variáveis ​​de ambiente reais, ele não será atualizado por esse script.
precisa saber é o seguinte
Olá @DieterDP, sua solução funciona para mim! Estou usando o Windows 10 em uma máquina de 64 bits. Eu recebo um erro: "ERRO: O sistema não conseguiu encontrar a chave ou o valor do registro especificado.". No entanto, a atualização das variáveis ​​de ambiente é bem-sucedida. De onde vem o erro?
27716 K.Mulier
Difícil dizer sem realmente testá-lo, mas acho que a estrutura do registro no W10 pode ser um pouco diferente. Se você quiser, tente caçar o erro executando os comandos na linha de comando.
21416 DieterDP
2

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

  • Set colocará o var na sua sessão atual
  • O SetX colocará o var no ambiente, mas NÃO na sua sessão atual

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 ...

Jeroen van Dijk-Jun
fonte
2

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.

Charles Grunwald
fonte
Eu gosto da sua resposta. Postar a mesma resposta para a minha pergunta aqui stackoverflow.com/q/61473551/1082063 e eu vou aceitá-la como a resposta. Obrigado.
David I. McIntosh
1

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 ...

PhiLho
fonte
1

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

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.

Andy McRae
fonte
0

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 ENDLOCALantes da saída do lote ou se for interrompido inesperadamente.

Quase todos os arquivos em lotes que escrevo começam, SETLOCALpois, 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 ENDLOCALaparência será assim:

ENDLOCAL & (
  SET RESULT1=%RESULT1%
  SET RESULT2=%RESULT2%
)
custódias
fonte
-1

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.

Jens Hykkelbjerg
fonte