Интерфейс для изменения переменных среды Windows из Python

Как я могу постоянно изменять переменные среды Windows из сценария Python? (это скрипт setup.py)

Я ищу стандартную функцию или модуль для этого. Я уже знаком с реестром , но любые комментарии по этому поводу также приветствуются.

6 Solutions collect form web for “Интерфейс для изменения переменных среды Windows из Python”

Использование setx имеет несколько недостатков, особенно если вы пытаетесь добавить к переменным среды (например, setx PATH% Path%; C: \ mypath). Это будет многократно добавляться к пути при каждом запуске, что может быть проблемой. Хуже того, он не различает путь машины (хранится в HKEY_LOCAL_MACHINE) и путь пользователя (хранится в HKEY_CURRENT_USER). Переменная среды, которую вы видите в командной строке, состоит из конкатенации этих двух значений. Следовательно, перед вызовом setx:

user PATH == u machine PATH == m %PATH% == m;u > setx PATH %PATH%;new Calling setx sets the USER path by default, hence now: user PATH == m;u;new machine PATH == m %PATH% == m;m;u;new 

Путь к системе неизбежно дублируется в переменной окружения% PATH% каждый раз, когда вы вызываете setx для добавления в PATH. Эти изменения являются постоянными, никогда не перезагружаются перезагрузками и поэтому накапливаются в течение всего срока службы машины.

Попытка компенсировать это в DOS выше моих возможностей. Поэтому я обратился к Питону. Решение, которое я придумал сегодня, чтобы установить переменные среды путем настройки реестра, включая добавление к PATH без введения дубликатов, выглядит следующим образом:

 from os import system, environ import win32con from win32gui import SendMessage from _winreg import ( CloseKey, OpenKey, QueryValueEx, SetValueEx, HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, KEY_ALL_ACCESS, KEY_READ, REG_EXPAND_SZ, REG_SZ ) def env_keys(user=True): if user: root = HKEY_CURRENT_USER subkey = 'Environment' else: root = HKEY_LOCAL_MACHINE subkey = r'SYSTEM\CurrentControlSet\Control\Session Manager\Environment' return root, subkey def get_env(name, user=True): root, subkey = env_keys(user) key = OpenKey(root, subkey, 0, KEY_READ) try: value, _ = QueryValueEx(key, name) except WindowsError: return '' return value def set_env(name, value): key = OpenKey(HKEY_CURRENT_USER, 'Environment', 0, KEY_ALL_ACCESS) SetValueEx(key, name, 0, REG_EXPAND_SZ, value) CloseKey(key) SendMessage( win32con.HWND_BROADCAST, win32con.WM_SETTINGCHANGE, 0, 'Environment') def remove(paths, value): while value in paths: paths.remove(value) def unique(paths): unique = [] for value in paths: if value not in unique: unique.append(value) return unique def prepend_env(name, values): for value in values: paths = get_env(name).split(';') remove(paths, '') paths = unique(paths) remove(paths, value) paths.insert(0, value) set_env(name, ';'.join(paths)) def prepend_env_pathext(values): prepend_env('PathExt_User', values) pathext = ';'.join([ get_env('PathExt_User'), get_env('PathExt', user=False) ]) set_env('PathExt', pathext) set_env('Home', '%HomeDrive%%HomePath%') set_env('Docs', '%HomeDrive%%HomePath%\docs') set_env('Prompt', '$P$_$G$S') prepend_env('Path', [ r'%SystemDrive%\cygwin\bin', # Add cygwin binaries to path r'%HomeDrive%%HomePath%\bin', # shortcuts and 'pass-through' bat files r'%HomeDrive%%HomePath%\docs\bin\mswin', # copies of standalone executables ]) # allow running of these filetypes without having to type the extension prepend_env_pathext(['.lnk', '.exe.lnk', '.py']) 

Это не влияет на текущий процесс или родительскую оболочку, но это повлияет на все окна cmd, открытые после его запуска, без необходимости перезагрузки, и их можно безопасно редактировать и повторно запускать много раз, не вводя никаких дубликатов.

Может быть так же просто использовать внешнюю команду setx Windows:

 C:\>set NEWVAR Environment variable NEWVAR not defined C:\>python Python 2.5.4 (r254:67916, Dec 23 2008, 15:10:54) [MSC v.1310 32 bit (Intel)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>> import os >>> os.system('setx NEWVAR newvalue') 0 >>> os.getenv('NEWVAR') >>> ^Z C:\>set NEWVAR Environment variable NEWVAR not defined 

Теперь откройте новую командную строку:

 C:\>set NEWVAR NEWVAR=newvalue 

Как вы можете видеть, setx не устанавливает переменную для текущего сеанса, ни для родительского процесса (первая командная строка). Но он постоянно устанавливает переменную в реестре для будущих процессов.

Я не думаю, что есть способ изменить среду родительского процесса вообще (и если есть, я бы хотел это услышать!).

Должно быть, было тысячелетие назад, что я попытался изменить среду текущего сеанса DOS с помощью программы. Проблема в том, что эта программа работает в пределах собственной оболочки DOS, поэтому она должна работать в своей родительской среде. Для поиска местоположения этой родительской среды требуется прогулка, начиная с Информационного блока DOS по всей цепочке блоков управления памятью. Как только я узнал, как это сделать, моя потребность в манипулировании переменными окружения исчезла. Я дам вам код Turbo Pascal ниже, но я думаю, что есть как минимум три лучших способа сделать трюк:

  1. Создайте командный файл, который: (a) вызывает сценарий Python (или что-то еще), который генерирует временный пакетный файл, содержащий соответствующие команды SET; (b) вызывает временный командный файл (команды SET выполняются в текущей оболочке); и (c) удаляет временный пакетный файл.

  2. Создайте скрипт Python, который записывает что-то вроде «VAR1 = val1 \ nVAR2 = val2 \ nVAR3 = val3 \ n» в STDOUT. Используйте его таким образом в своем пакетном файле:

    for /f "delims=|" %%X in (' for /f "delims=|" %%X in (' callYourPythonScript ') do set %%X

    et voilà: переменные VAR1, VAR2 и VAR3 получили значение.

  3. Измените реестр Windows и передайте изменения настроек, как описано здесь Александром Прокофьевым.

И здесь идет код Pascal (вам может понадобиться голландский словарь и книга программирования Pascal) программы, которая просто сообщает о местах памяти. Он по-прежнему работает под Windows XP, сообщают ли мы, что мы запускаем DOS 5.00. Это только первое начало, для управления выбранной средой требуется много программирования низкого уровня. И поскольку структура указателя может показаться правильной, я не уверен, что модель среды 1994 года по-прежнему сохраняется в эти дни …

 program MCBKETEN; uses dos, HexConv; {----------------------------------------------------------------------------} { Programma: MCBKETEN.EXE } { Broncode : MCBKETEN.PAS } { Doel : Tocht langs de MCB's met rapportage } { Datum : 11 januari 1994 } { Auteur : Meindert Meindertsma } { Versie : 1.00 } {----------------------------------------------------------------------------} type MCB_Ptr = ^MCB; { MCB_PtrPtr = ^MCB_Ptr; vervallen wegens DOS 2.11 -- zie verderop } MCB = record Signatuur : char; Eigenaar : word; Paragrafen : word; Gereserveerd : array[1..3] of byte; Naam : array[1..8] of char; end; BlokPtr = ^BlokRec; BlokRec = record Vorige : BlokPtr; DitSegment, Paragrafen : word; Signatuur : string[6]; Eigenaar, Omgeving : word; Functie : String4; Oorsprong, Pijl : char; KorteNaam : string[8]; LangeNaam : string; Volgende : BlokPtr; end; PSP_Ptr = ^PSP; PSP = record Vulsel1 : array[1..44] of byte; Omgeving : word; Vulsel2 : array[47..256] of byte; end; var Zone : string[5]; ProgGevonden, EindeKeten, Dos3punt2 : boolean; Regs : registers; ActMCB : MCB_Ptr; EersteSchakel, Schakel, LaatsteSchakel : BlokPtr; ActPSP : PSP_Ptr; EersteProg, Meester, Ouder, TerugkeerSegment, TerugkeerOffset, TerugkeerSegment2, OuderSegment : word; Specificatie : string[8]; ReleaseNummer : string[2]; i : byte; {----------------------------------------------------------------------------} { PROCEDURES EN FUNCTIES } {----------------------------------------------------------------------------} function Coda (Omgeving : word; Paragrafen : word) : string; var i : longint; Vorige, Deze : char; Streng : string; begin i := 0; Deze := #0; repeat Vorige := Deze; Deze := char (ptr (Omgeving, i)^); inc (i); until ((Vorige = #0) and (Deze = #0)) or (i div $10 >= Paragrafen); if (i + 3) div $10 < Paragrafen then begin Vorige := char (ptr (Omgeving, i)^); inc (i); Deze := char (ptr (Omgeving, i)^); inc (i); if (Vorige = #01) and (Deze = #0) then begin Streng := ''; Deze := char (ptr (Omgeving, i)^); inc (i); while (Deze <> #0) and (i div $10 < Paragrafen) do begin Streng := Streng + Deze; Deze := char (ptr (Omgeving, i)^); inc (i); end; Coda := Streng; end else Coda := ''; end else Coda := ''; end {Coda}; {----------------------------------------------------------------------------} { HOOFDPROGRAMMA } {----------------------------------------------------------------------------} BEGIN {----- Initiatie -----} Zone := 'Lower'; ProgGevonden := FALSE; EindeKeten := FALSE; Dos3punt2 := (dosversion >= $1403) and (dosversion <= $1D03); Meester := $0000; Ouder := $0000; Specificatie[0] := #8; str (hi (dosversion) : 2, ReleaseNummer); if ReleaseNummer[1] = ' ' then ReleaseNummer[1] := '0'; {----- Pointer naar eerste MCB ophalen ------} Regs.AH := $52; { functie $52 geeft adres van DOS Info Block in ES:BX } msdos (Regs); { ActMCB := MCB_PtrPtr (ptr (Regs.ES, Regs.BX - 4))^; NIET onder DOS 2.11 } ActMCB := ptr (word (ptr (Regs.ES, Regs.BX - 2)^), $0000); {----- MCB-keten doorlopen -----} new (EersteSchakel); EersteSchakel^.Vorige := nil; Schakel := EersteSchakel; repeat with Schakel^ do begin DitSegment := seg (ActMCB^); Paragrafen := ActMCB^.Paragrafen; if DitSegment + Paragrafen >= $A000 then Zone := 'Upper'; Signatuur := Zone + ActMCB^.Signatuur; Eigenaar := ActMCB^.Eigenaar; ActPSP := ptr (Eigenaar, 0); if not ProgGevonden then EersteProg := DitSegment + 1; if Eigenaar >= EersteProg then Omgeving := ActPSP^.Omgeving else Omgeving := 0; if DitSegment + 1 = Eigenaar then begin ProgGevonden := TRUE; Functie := 'Prog'; KorteNaam[0] := #0; while (ActMCB^.Naam[ ord (KorteNaam[0]) + 1 ] <> #0) and (KorteNaam[0] < #8) do begin inc (KorteNaam[0]); KorteNaam[ ord (KorteNaam[0]) ] := ActMCB^.Naam[ ord (KorteNaam[0]) ]; end; if Eigenaar = prefixseg then begin TerugkeerSegment := word (ptr (prefixseg, $000C)^); TerugkeerOffset := word (ptr (prefixseg, $000A)^); LangeNaam := '-----> Terminate Vector = ' + WordHex (TerugkeerSegment) + ':' + WordHex (TerugkeerOffset ) ; end else LangeNaam := ''; end {if ÆProgØ} else begin if Eigenaar = $0008 then begin if ActMCB^.Naam[1] = 'S' then case ActMCB^.Naam[2] of 'D' : Functie := 'SysD'; 'C' : Functie := 'SysP'; else Functie := 'Data'; end {case} else Functie := 'Data'; KorteNaam := ''; LangeNaam := ''; end {if Eigenaar = $0008} else begin if DitSegment + 1 = Omgeving then begin Functie := 'Env '; LangeNaam := Coda (Omgeving, Paragrafen); if EersteProg = Eigenaar then Meester := Omgeving; end {if ÆEnvØ} else begin move (ptr (DitSegment + 1, 0)^, Specificatie[1], 8); if (Specificatie = 'PATH=' + #0 + 'CO') or (Specificatie = 'COMSPEC=' ) or (Specificatie = 'OS=DRDOS' ) then begin Functie := 'Env' + chr (39); LangeNaam := Coda (DitSegment + 1, Paragrafen); if (EersteProg = Eigenaar) and (Meester = $0000 ) then Meester := DitSegment + 1; end else begin if Eigenaar = 0 then Functie := 'Free' else Functie := 'Data'; LangeNaam := ''; if (EersteProg = Eigenaar) and (Meester = $0000 ) then Meester := DitSegment + 1; end; end {else: not ÆEnvØ}; KorteNaam := ''; end {else: Eigenaar <> $0008}; end {else: not ÆProgØ}; {----- KorteNaam redigeren -----} for i := 1 to length (KorteNaam) do if KorteNaam[i] < #32 then KorteNaam[i] := '.'; KorteNaam := KorteNaam + ' '; {----- Oorsprong vaststellen -----} if EersteProg = Eigenaar then Oorsprong := '*' else Oorsprong := ' '; {----- Actueel proces (uitgaande Pijl) vaststellen -----} if Eigenaar = prefixseg then Pijl := '>' else Pijl := ' '; end {with Schakel^}; {----- MCB-opeenvolging onderzoeken / schakelverloop vaststellen -----} if (Zone = 'Upper') and (ActMCB^.Signatuur = 'Z') then begin Schakel^.Volgende := nil; EindeKeten := TRUE; end else begin ActMCB := ptr (seg (ActMCB^) + ActMCB^.Paragrafen + 1, 0); if ((ActMCB^.Signatuur <> 'M') and (ActMCB^.Signatuur <> 'Z')) or ($FFFF - ActMCB^.Paragrafen < seg (ActMCB^) ) then begin Schakel^.Volgende := nil; EindeKeten := TRUE; end else begin new (LaatsteSchakel); Schakel^.Volgende := LaatsteSchakel; LaatsteSchakel^.Vorige := Schakel; Schakel := LaatsteSchakel; end {else: (ÆMØ or ÆZØ) and Æteveel_ParagrafenØ}; end {else: ÆLowerØ or not ÆZØ}; until EindeKeten; {----- Terugtocht -----} while Schakel <> nil do with Schakel^ do begin {----- Ouder-proces vaststellen -----} TerugkeerSegment2 := TerugkeerSegment + (TerugkeerOffset div $10); if (DitSegment <= TerugkeerSegment2) and (DitSegment + Paragrafen >= TerugkeerSegment2) then OuderSegment := Eigenaar; {----- Meester-omgeving markeren -----} if DitSegment + 1 = Meester then Oorsprong := 'M'; {----- Schakel-verloop -----} Schakel := Schakel^.Vorige; end {while Schakel <> nil}; {----- Rapportage -----} writeln ('Chain of Memory Control Blocks in DOS version ', lo (dosversion), '.', ReleaseNummer, ':'); writeln; writeln ('MCB@ #Par Signat PSP@ Env@ Type !! Name File'); writeln ('---- ---- ------ ---- ---- ---- -- -------- ', '-----------------------------------'); Schakel := EersteSchakel; while Schakel <> nil do with Schakel^ do begin {----- Ouder-omgeving vaststellen -----} if Eigenaar = OuderSegment then begin if not Dos3punt2 then begin if (Functie = 'Env ') then begin Ouder := DitSegment + 1; Pijl := 'Û'; end else Pijl := '<'; end {if not Dos3punt2} else begin if ((Functie = 'Env' + chr (39)) or (Functie = 'Data')) and (Ouder = $0000) then begin Ouder := DitSegment + 1; Pijl := 'Û'; end else Pijl := '<'; end {else: Dos3punt2}; end {with Schakel^}; {----- Keten-weergave -----} writeln (WordHex (DitSegment) , ' ', WordHex (Paragrafen) , ' ', Signatuur , ' ', WordHex (Eigenaar) , ' ', WordHex (Omgeving) , ' ', Functie , ' ', Oorsprong, Pijl , ' ', KorteNaam , ' ', LangeNaam ); {----- Schakel-verloop -----} Schakel := Schakel^.Volgende; end {while Schakel <> nil}; {----- Afsluiting rapportage -----} writeln; write ('* = First command interpreter at '); if ProgGevonden then writeln (WordHex (EersteProg), ':0000') else writeln ('?'); write ('M = Master environment at '); if Meester > $0000 then writeln (WordHex (Meester), ':0000') else writeln ('?'); write ('< = Parent proces at '); writeln (WordHex (OuderSegment), ':0000'); write ('Û = Parent environment at '); if Ouder > $0000 then writeln (WordHex (Ouder), ':0000') else writeln ('?'); writeln ('> = Current proces at ', WordHex (prefixseg), ':0000'); writeln (' returns to ', WordHex (TerugkeerSegment), ':', WordHex (TerugkeerOffset)); END. 

(Выше ASCII 127, в этой презентации могут быть некоторые проблемы перевода ASCII / ANSI).

Путь к реестру – это если вы хотите изменить его навсегда для всего, что я думаю, это то, что вы хотите здесь, так как оно находится в setup.py.

Временно для вашего процесса, тогда os.environ – это трюк.

В модуле os есть функции getenv и putenv. Однако кажется, что putenv работает неправильно и что вы должны использовать реестр Windows вместо этого

Посмотрите на эту дискуссию

Этот скрипт Python [*] пытается изменить GLOBAL-env-vars в реестре, если разрешения не разрешаются в реестре пользователя, а затем уведомляет обо всех окнах об изменении:

 """ Show/Modify/Append registry env-vars (ie `PATH`) and notify Windows-applications to pickup changes. First attempts to show/modify HKEY_LOCAL_MACHINE (all users), and if not accessible due to admin-rights missing, fails-back to HKEY_CURRENT_USER. Write and Delete operations do not proceed to user-tree if all-users succeed. Syntax: {prog} : Print all env-vars. {prog} VARNAME : Print value for VARNAME. {prog} VARNAME VALUE : Set VALUE for VARNAME. {prog} +VARNAME VALUE : Append VALUE in VARNAME delimeted with ';' (ie used for `PATH`). {prog} -VARNAME : Delete env-var value. Note that the current command-window will not be affected, changes would apply only for new command-windows. """ import winreg import os, sys, win32gui, win32con def reg_key(tree, path, varname): return '%s\%s:%s' % (tree, path, varname) def reg_entry(tree, path, varname, value): return '%s=%s' % (reg_key(tree, path, varname), value) def query_value(key, varname): value, type_id = winreg.QueryValueEx(key, varname) return value def show_all(tree, path, key): i = 0 while True: try: n,v,t = winreg.EnumValue(key, i) print(reg_entry(tree, path, n, v)) i += 1 except OSError: break ## Expected, this is how iteration ends. def notify_windows(action, tree, path, varname, value): win32gui.SendMessage(win32con.HWND_BROADCAST, win32con.WM_SETTINGCHANGE, 0, 'Environment') print("---%s %s" % (action, reg_entry(tree, path, varname, value))) def manage_registry_env_vars(varname=None, value=None): reg_keys = [ ('HKEY_LOCAL_MACHINE', r'SYSTEM\CurrentControlSet\Control\Session Manager\Environment'), ('HKEY_CURRENT_USER', r'Environment'), ] for (tree_name, path) in reg_keys: tree = eval('winreg.%s'%tree_name) try: with winreg.ConnectRegistry(None, tree) as reg: with winreg.OpenKey(reg, path, 0, winreg.KEY_ALL_ACCESS) as key: if not varname: show_all(tree_name, path, key) else: if not value: if varname.startswith('-'): varname = varname[1:] value = query_value(key, varname) winreg.DeleteValue(key, varname) notify_windows("Deleted", tree_name, path, varname, value) break ## Don't propagate into user-tree. else: value = query_value(key, varname) print(reg_entry(tree_name, path, varname, value)) else: if varname.startswith('+'): varname = varname[1:] value = query_value(key, varname) + ';' + value winreg.SetValueEx(key, varname, 0, winreg.REG_EXPAND_SZ, value) notify_windows("Updated", tree_name, path, varname, value) break ## Don't propagate into user-tree. except PermissionError as ex: print("!!!Cannot access %s due to: %s" % (reg_key(tree_name, path, varname), ex)) except FileNotFoundError as ex: print("!!!Cannot find %s due to: %s" % (reg_key(tree_name, path, varname), ex)) if __name__=='__main__': args = sys.argv argc = len(args) if argc > 3: print(__doc__.format(prog=args[0])) sys.exit() manage_registry_env_vars(*args[1:]) 

Ниже приведены примеры использования, предполагая, что он был сохранен в файле с именем setenv.py где-то в вашем текущем пути. Обратите внимание, что в этих примерах у меня не было прав администратора , поэтому изменения повлияли только на дерево реестра моего локального пользователя:

 > REM ## Print all env-vars > setenv.py !!!Cannot access HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment:PATH due to: [WinError 5] Access is denied HKEY_CURRENT_USER\Environment:PATH=... ... > REM ## Query env-var: > setenv.py PATH C:\foo !!!Cannot access HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment:PATH due to: [WinError 5] Access is denied !!!Cannot find HKEY_CURRENT_USER\Environment:PATH due to: [WinError 2] The system cannot find the file specified > REM ## Set env-var: > setenv.py PATH C:\foo !!!Cannot access HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment:PATH due to: [WinError 5] Access is denied ---Set HKEY_CURRENT_USER\Environment:PATH=C:\foo > REM ## Append env-var: > setenv.py +PATH D:\Bar !!!Cannot access HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment:PATH due to: [WinError 5] Access is denied ---Set HKEY_CURRENT_USER\Environment:PATH=C:\foo;D:\Bar > REM ## Delete env-var: > setenv.py -PATH !!!Cannot access HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment:PATH due to: [WinError 5] Access is denied ---Deleted HKEY_CURRENT_USER\Environment:PATH 

[*] Адаптировано из: http://code.activestate.com/recipes/416087-persistent-environment-variables-on-windows/

  • Почему я получаю SyntaxError для escape-кода Unicode в моем пути к файлу?
  • Официальный установщик Python отсутствует python27.dll
  • Листинг серийных (COM) портов в Windows?
  • Изменение «предпочтительной кодировки языка» в Python 3 в Windows
  • Доступ к использованию ЦП / ОЗУ (например, с Диспетчером задач, но через API!)?
  • специфичная для платформы семантика Unicode в Python 2.7
  • Установка проблемы Pylint с окнами и python 3.2
  • Речевой модуль python 3.x
  •  
    Interesting Posts for Van-Lav

    Распространение документа в Gensim LDA

    Как рассчитывается scikit-learn GridSearchCV best_score_?

    Как использовать PyObject_IsInstance с не встроенным классом в качестве второго аргумента?

    изменить размер массива 2D numpy, исключая NaN

    который является наиболее эффективным способом загрузки набора данных JSON в Pandas DataFrames

    Как заставить ширину поля заголовка охватывать весь участок?

    Как получить статус сервера Asterisk с помощью Socket – Python

    Есть ли способ использовать функцию strftime как даты до 1900 года в Python?

    Eclipse и python: библиотека будет импортироваться в interprer, но не в IDE

    Извлеките веб-службу SAS Stored Process в Python и сохраните ее в кадре данных

    Как запускать команды управления Django в Google Cloud SQL

    Несколько обработчиков исключений для одного и того же исключения

    Обнаружение вывода консоли javascript с помощью python

    Django: как установить уровень журнала для INFO или DEBUG

    Pandas: создайте новый кадр данных, используя несколько результатов GroupBy

    Python - лучший язык программирования в мире.