Jeśli zmodyfikuję lub dodam zmienną środowiskową, muszę ponownie uruchomić wiersz polecenia. Czy istnieje polecenie, które można wykonać, aby zrobić to bez ponownego uruchamiania CMD?
Jeśli zmodyfikuję lub dodam zmienną środowiskową, muszę ponownie uruchomić wiersz polecenia. Czy istnieje polecenie, które można wykonać, aby zrobić to bez ponownego uruchamiania CMD?
Odpowiedzi:
Możesz przechwycić systemowe zmienne środowiskowe za pomocą skryptu VBS, ale potrzebujesz skryptu nietoperza, aby faktycznie zmienić bieżące zmienne środowiskowe, więc jest to rozwiązanie łączone.
Utwórz plik o nazwie resetvars.vbs
zawierającej ten kod i zapisz go na ścieżce:
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
utwórz inną nazwę pliku resetvars.bat zawierającą ten kod, tę samą lokalizację:
@echo off
%~dp0resetvars.vbs
call "%TEMP%\resetvars.bat"
Jeśli chcesz odświeżyć zmienne środowiskowe, po prostu uruchom resetvars.bat
Apologetyka :
Dwa główne problemy, jakie wpadłem na to rozwiązanie, to:
za. Nie mogłem znaleźć prostego sposobu na eksport zmiennych środowiskowych ze skryptu vbs z powrotem do wiersza poleceń i
b. zmienna środowiskowa PATH jest konkatenacją użytkownika i systemowych zmiennych PATH.
Nie jestem pewien, jaka jest ogólna reguła dla sprzecznych zmiennych między użytkownikiem a systemem, dlatego zdecydowałem się na zastąpienie systemu przez użytkownika, z wyjątkiem zmiennej PATH, która jest obsługiwana konkretnie.
Używam dziwnego mechanizmu VBS + nietoperz + tymczasowy nietoperz, aby obejść problem eksportowania zmiennych z VBS.
Uwaga : ten skrypt nie usuwa zmiennych.
Można to prawdopodobnie poprawić.
DODANY
Jeśli chcesz wyeksportować środowisko z jednego okna cmd do drugiego, użyj tego skryptu (nazwijmy go 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
Uruchom exportvars.vbs
w oknie chcesz wyeksportować z , a następnie przejść do okna, które chcesz wyeksportować do i wpisz:
"%TEMP%\resetvars.bat"
Oto, czego używa 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
aby zaktualizować zmienne środowiskowe do bieżącej sesji.
Powershell
? Wydaje się, że działa tylko cmd.exe
dla mnie.
W systemie Windows 7/8/10 możesz zainstalować Chocolatey, który ma skrypt dla tego wbudowanego.
Po zainstalowaniu Chocolatey po prostu wpisz refreshenv
.
Z założenia nie ma wbudowanego mechanizmu dla systemu Windows do propagowania dodawania / zmiany / usuwania zmiennej środowiskowej do już uruchomionego cmd.exe, z innego cmd.exe lub z „Mój komputer -> Właściwości -> Ustawienia zaawansowane -> Zmienne środowiska".
Jeśli zmodyfikujesz lub dodasz nową zmienną środowiskową poza zakresem istniejącego otwartego wiersza polecenia, musisz albo zrestartować wiersz polecenia, albo ręcznie dodać za pomocą SET w istniejącym wierszu polecenia.
Te ostatnie Zaakceptowanych odpowiedź przedstawia częściowy obejść poprzez ręczne odświeżanie wszystkie zmienne środowiskowe w skrypcie. Skrypt obsługuje przypadek użycia globalnej zmiany zmiennych środowiskowych w „Mój komputer ... Zmienne środowiskowe”, ale jeśli zmienna środowiskowa zostanie zmieniona w jednym cmd.exe, skrypt nie propaguje jej do innego działającego cmd.exe.
Natknąłem się na tę odpowiedź, zanim w końcu znalazłem łatwiejsze rozwiązanie.
Po prostu uruchom ponownie explorer.exe
w Menedżerze zadań.
Nie testowałem, ale może być konieczne ponowne otwarcie wiersza polecenia.
Podziękowania dla Timo Huovinena tutaj: Węzeł nie został rozpoznany, chociaż został pomyślnie zainstalowany (jeśli to pomogło, proszę podać informację o komentarzu tego człowieka).
cmd
okno jako Administrator. Użyj polecenia taskkill /f /im explorer.exe && explorer.exe
. To zabije proces explorer.exe i uruchom go ponownie.
Działa to w systemie Windows 7: SET PATH=%PATH%;C:\CmdShortcuts
przetestowane przez wpisanie echa% PATH% i zadziałało, w porządku. ustaw także, jeśli otworzysz nowe cmd, nie ma już potrzeby tych nieznośnych restartów :)
setx
ponieważ dziedziczy bieżące środowisko, które może mieć zmienione zmienne, a nie to, czego chcę na stałe. Wykonanie tego w ten sposób pozwala mi uniknąć ponownego uruchamiania konsoli w celu użycia zmiennych, unikając jednocześnie problemu braku ich globalnej dostępności w przyszłości.
Użyj „setx” i uruchom ponownie polecenie cmd
Dla tego zadania istnieje narzędzie wiersza polecenia o nazwie „ setx ”. Służy do odczytu i zapisu zmiennych env. Zmienne są zachowywane po zamknięciu okna poleceń.
„Tworzy lub modyfikuje zmienne środowiskowe w środowisku użytkownika lub systemu, bez konieczności programowania lub pisania skryptów. Komenda setx pobiera również wartości kluczy rejestru i zapisuje je w plikach tekstowych”.
Uwaga: zmienne utworzone lub zmodyfikowane przez to narzędzie będą dostępne w przyszłych oknach poleceń, ale nie w bieżącym oknie poleceń CMD.exe. Musisz więc zrestartować.
Jeśli setx
brakuje:
Lub zmodyfikuj rejestr
MSDN mówi:
Aby programowo dodać lub zmodyfikować systemowe zmienne środowiskowe, dodaj je do klucza rejestru HKEY_LOCAL_MACHINE \ System \ CurrentControlSet \ Control \ Session Manager \ Environment , a następnie wyślij komunikat WM_SETTINGCHANGE z parametrem lParam ustawionym na ciąg „ Środowisko ”.
Umożliwia to aplikacjom, takim jak powłoka, pobieranie aktualizacji.
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment\VARIABLE
obecne środowisko użytkownika: HKEY_CURRENT_USER\Environment\VARIABLE
%PATH%
czas, setx
możesz go skrócić do 1024 bajtów! I tak po prostu jego wieczór zniknął
Wywołanie tej funkcji zadziałało dla mnie:
VOID Win32ForceSettingsChange()
{
DWORD dwReturnValue;
::SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, (LPARAM) "Environment", SMTO_ABORTIFHUNG, 5000, &dwReturnValue);
}
Najlepszą metodą, jaką wymyśliłem, było wykonanie zapytania do rejestru. Oto mój przykład.
W moim przykładzie wykonałem instalację przy użyciu pliku Batch, który dodał nowe zmienne środowiskowe. Musiałem to zrobić, gdy tylko instalacja się zakończy, ale nie byłem w stanie odrodzić nowego procesu z tymi nowymi zmiennymi. Przetestowałem odradzanie innego okna eksploratora i zadzwoniłem z powrotem do cmd.exe i działało to, ale w Vista i Windows 7 Explorer działa tylko jako jedna instancja i normalnie jako osoba zalogowana. Nie udałoby się to z automatyzacją, ponieważ potrzebuję moich uprawnień administratora, aby rób rzeczy niezależnie od uruchamiania z systemu lokalnego lub jako administrator w pudełku. Ograniczeniem jest to, że nie obsługuje rzeczy takich jak ścieżka, działało to tylko na prostych zmiennych środowiskowych. To pozwoliło mi użyć partii do przejścia do katalogu (ze spacjami) i skopiowania plików do uruchomienia .exes itp. To zostało napisane dzisiaj z zasobów maja na stackoverflow.com
Połączenia z oryginalną partią do nowej partii:
testenvget.cmd SDROOT (lub jakakolwiek zmienna)
@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
Jest też inna metoda, którą wymyśliłem z różnych różnych pomysłów. Patrz poniżej. To w zasadzie pobierze najnowszą zmienną ścieżki z rejestru, jednak spowoduje to szereg problemów, ponieważ zapytanie rejestru poda zmienne samo w sobie, co oznacza, że wszędzie tam, gdzie jest zmienna, to nie zadziała, więc aby zwalczyć ten problem I w zasadzie podwajaj ścieżkę. Bardzo paskudny. Bardziej udoskonaloną metodą byłoby: Ustaw ścieżkę =% ścieżki%; C: \ Program Files \ Software .... \
Bez względu na to jest nowy plik wsadowy, należy zachować ostrożność.
@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
Najłatwiejszym sposobem dodania zmiennej do ścieżki bez ponownego uruchamiania dla bieżącej sesji jest otwarcie wiersza polecenia i wpisanie:
PATH=(VARIABLE);%path%
i naciśnij enter .
aby sprawdzić, czy zmienna została załadowana, wpisz
PATH
i naciśnij enter. Jednak zmienna będzie tylko częścią ścieżki, dopóki nie uruchomisz się ponownie.
Można to zrobić, zastępując tabelę środowiska w ramach określonego procesu.
Jako dowód koncepcji napisałem tę przykładową aplikację, która właśnie edytowała jedną (znaną) zmienną środowiskową w procesie 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;
}
Przykładowe dane wyjściowe:
>set ENVTEST=abc
>cppTest.exe 13796
Target PID: 13796
PEB: 000007FFFFFD3000
UserProcessParameters: 00000000004B2F30
environment: 000000000052E700
Offset: 1528
>set ENVTEST
ENVTEST=def
Takie podejście byłoby również ograniczone do ograniczeń bezpieczeństwa. Jeśli cel jest uruchamiany na wyższym poziomie lub na wyższym koncie (takim jak SYSTEM), nie mielibyśmy uprawnień do edycji jego pamięci.
Jeśli chcesz to zrobić w aplikacji 32-bitowej, powyższe przesunięcia kodowane na stałe zmieniłyby się odpowiednio na 0x10 i 0x48. Te przesunięcia można znaleźć, usuwając struktury _PEB i _RTL_USER_PROCESS_PARAMETERS w debuggerze (np. W WinDbg dt _PEB
idt _RTL_USER_PROCESS_PARAMETERS
)
Aby zmienić proof-of-concept na to, czego potrzebuje OP, wystarczy wyliczyć bieżące zmienne systemowe i środowiskowe użytkownika (takie jak udokumentowane odpowiedzią @ tsadok) i zapisać całą tabelę środowiska w pamięci procesu docelowego.
Edycja: Rozmiar bloku środowiska jest również przechowywany w strukturze _RTL_USER_PROCESS_PARAMETERS, ale pamięć jest przydzielana na stercie procesu. Zatem z procesu zewnętrznego nie mielibyśmy możliwości zmiany jego rozmiaru i powiększenia. Bawiłem się przy użyciu VirtualAllocEx do przydzielenia dodatkowej pamięci w procesie docelowym do przechowywania środowiska, i byłem w stanie ustawić i odczytać zupełnie nową tabelę. Niestety każda próba modyfikacji środowiska z normalnych środków ulegnie awarii i zostanie spalona, ponieważ adres nie będzie już wskazywał na stos (spowoduje awarię w RtlSizeHeap).
Zmienne środowiskowe są przechowywane w HKEY_LOCAL_MACHINE \ SYSTEM \ ControlSet \ Control \ Session Manager \ Environment.
Wiele przydatnych zmiennych środowisk, takich jak Ścieżka, jest przechowywanych jako REG_SZ. Istnieje kilka sposobów dostępu do rejestru, w tym REGEDIT:
REGEDIT /E <filename> "HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Session Manager\Environment"
Wyjście zaczyna się od magicznych liczb. Aby go wyszukać za pomocą polecenia find, należy wpisać i przekierować:type <filename> | findstr -c:\"Path\"
Jeśli więc chcesz odświeżyć zmienną ścieżki w bieżącej sesji poleceń za pomocą właściwości systemowych, następujący skrypt wsadowy działa dobrze:
RefreshPath.cmd:
@echo wyłączone REM To rozwiązanie wymaga podniesienia uprawnień w celu odczytania z rejestru. jeśli istnieją% temp% \ env.reg del% temp% \ env.reg / q / f REGEDIT / E% temp% \ env.reg "HKEY_LOCAL_MACHINE \ SYSTEM \ ControlSet001 \ Control \ Session Manager \ Environment" jeśli nie istnieje% temp% \ env.reg ( echo „Nie można zapisać rejestru do lokalizacji tymczasowej” wyjście 1 ) SETLOCAL EnableDelayedExpansion dla / f "tokenów = 1,2 * delims ==" %% i in ('type% temp% \ env.reg ^ | findstr -c: \ "Path \" =') do ( set upath = %% ~ j echo! upath: \\ = \! >% temp% \ newpath ) ENDLOCAL for / f "tokens = *" %% i in (% temp% \ newpath) ustaw ścieżkę = %% i
Mylące może być to, że istnieje kilka miejsc, od których można zacząć cmd. W moim przypadku uruchomiłem cmd z Eksploratora Windows i zmienne środowiskowe nie uległy zmianie, a podczas uruchamiania cmd z „uruchom” (klawisz Windows + r) zmienne środowiskowe zostały zmienione .
W moim przypadku musiałem po prostu zabić proces eksploratora Windows z menedżera zadań, a następnie ponownie uruchomić go z menedżera zadań .
Kiedy to zrobiłem, miałem dostęp do nowej zmiennej środowiskowej z cmd, która została spawnowana z Eksploratora Windows.
Używam następującego kodu w skryptach wsadowych:
if not defined MY_ENV_VAR (
setx MY_ENV_VAR "VALUE" > nul
set MY_ENV_VAR=VALUE
)
echo %MY_ENV_VAR%
Używając SET po SETX można bezpośrednio używać zmiennej „lokalnej” bez ponownego uruchamiania okna poleceń. A przy następnym uruchomieniu zostanie użyta zmienna środowiskowa.
Podobało mi się podejście czekoladowe, jak podano w odpowiedzi anonimowego tchórza, ponieważ jest to podejście czysto wsadowe. Pozostawia jednak plik tymczasowy i niektóre zmienne tymczasowe. Zrobiłem dla siebie czystszą wersję.
Zrób plik refreshEnv.bat
gdzieś na swoim PATH
. Odśwież środowisko konsoli, wykonując 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.
Jeśli dotyczy tylko jednego (lub kilku) konkretnych zmiennych, które chcesz zmienić, myślę, że najłatwiejszym sposobem jest obejście tego problemu : po prostu ustaw w swoim środowisku ORAZ w bieżącej sesji konsoli
Mam ten prosty skrypt wsadowy, aby zmienić mój Maven z Java7 na Java8 (które są oba w środowisku zmiennym). Folder wsadowy znajduje się w mojej zmiennej PATH, więc zawsze mogę wywołać „ j8 ” oraz w konsoli i w środowisku mój JAVA_HOME var ulega zmianie:
j8.bat:
@echo off
set JAVA_HOME=%JAVA_HOME_8%
setx JAVA_HOME "%JAVA_HOME_8%"
Do tej pory uważam, że to działa najlepiej i najłatwiej. Prawdopodobnie chcesz, aby było to w jednym poleceniu, ale po prostu nie ma go w systemie Windows ...
Rozwiązanie, z którego korzystam od kilku lat:
@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
Edycja: Woops, oto zaktualizowana wersja.
Nie ma prostej drogi, jak powiedział Kev. W większości przypadków łatwiej jest odrodzić kolejne pole CMD. Co bardziej denerwujące, uruchomione programy również nie są świadome zmian (chociaż IIRC może mieć komunikat do wysłania, aby powiadomić o takiej zmianie).
Gorzej: w starszych wersjach systemu Windows trzeba było się wylogować, a następnie zalogować ponownie, aby uwzględnić zmiany ...
Używam tego skryptu Powershell, aby dodać do zmiennej PATH . Uważam, że przy niewielkiej korekcie może również działać w twoim przypadku.
#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%"
Dziękujemy za opublikowanie tego pytania, które jest dość interesujące, nawet w 2019 r. (Rzeczywiście, odnowienie powłoki cmd nie jest łatwe, ponieważ jest to pojedyncze wystąpienie, jak wspomniano powyżej), ponieważ odnawianie zmiennych środowiskowych w systemie Windows pozwala na wykonanie wielu zadań automatyzacji bez konieczność ręcznego ponownego uruchomienia wiersza polecenia.
Na przykład używamy tego, aby umożliwić wdrażanie i konfigurowanie oprogramowania na dużej liczbie komputerów, które regularnie instalujemy. I muszę przyznać, że ponowne uruchomienie wiersza poleceń podczas wdrażania naszego oprogramowania byłoby bardzo niepraktyczne i wymagałoby od nas znalezienia obejść, które niekoniecznie są przyjemne. Przejdźmy do naszego problemu. Postępujemy w następujący sposób.
1 - Mamy skrypt wsadowy, który z kolei wywołuje taki skrypt PowerShell
[plik: zadanie.cmd] .
cmd > powershell.exe -executionpolicy unrestricted -File C:\path_here\refresh.ps1
2 - Następnie skrypt refresh.ps1 odnawia zmienne środowiskowe za pomocą kluczy rejestru (GetValueNames () itp.). Następnie, w tym samym skrypcie PowerShell, musimy wywołać nowe dostępne zmienne środowiskowe. Na przykład, w typowym przypadku, jeśli przed chwilą zainstalowaliśmy nodeJS z cmd przy użyciu cichych poleceń, po wywołaniu funkcji możemy bezpośrednio wywołać npm w celu zainstalowania, w tej samej sesji, określonych pakietów, takich jak poniżej.
[plik: 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
Po zakończeniu skryptu PowerScript skrypt cmd wykonuje inne zadania. Teraz należy pamiętać, że po zakończeniu zadania cmd nadal nie ma dostępu do nowych zmiennych środowiskowych, nawet jeśli skrypt PowerShell zaktualizował je w swojej sesji. Dlatego wykonujemy wszystkie potrzebne zadania w skrypcie PowerShell, który może wywoływać te same polecenia co cmd.
Edycja: działa to tylko wtedy, gdy zmiany środowiska, które wprowadzasz, są wynikiem uruchomienia pliku wsadowego.
Jeśli plik wsadowy zaczyna się od, SETLOCAL
to zawsze wyjdzie z powrotem do oryginalnego środowiska przy wyjściu, nawet jeśli zapomnisz zadzwonićENDLOCAL
przed wyjściem z partii lub jeśli niespodziewanie zostanie przerwana.
Prawie każdy plik wsadowy, który piszę, zaczyna się SETLOCAL
od, ponieważ w większości przypadków nie chcę, aby skutki uboczne zmian środowiska pozostały. W przypadkach, w których chcę, aby pewne zmiany zmiennych środowiskowych były propagowane poza plikiem wsadowym, moje ostatnie ENDLOCAL
wygląda następująco:
ENDLOCAL & (
SET RESULT1=%RESULT1%
SET RESULT2=%RESULT2%
)
Aby rozwiązać ten problem, zmieniłem zmienną środowiskową za pomocą ZARÓWNO setx i set, a następnie zrestartowałem wszystkie instancje explorer.exe. W ten sposób każdy później uruchomiony proces będzie miał nową zmienną środowiskową.
Mój skrypt wsadowy, aby to zrobić:
setx /M ENVVAR "NEWVALUE"
set ENVVAR="NEWVALUE"
taskkill /f /IM explorer.exe
start explorer.exe >nul
exit
Problem z tym podejściem polega na tym, że wszystkie otwarte okna eksploratora zostaną zamknięte, co jest prawdopodobnie złym pomysłem - ale zobacz post Kev, aby dowiedzieć się, dlaczego jest to konieczne