일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- 배당
- 나스닥
- homebridge
- 국내주식
- 힐스테이트 광교산
- raspberry pi
- ConnectedHomeIP
- 코스피
- esp32
- 엔비디아
- RS-485
- MQTT
- matter
- 매터
- Bestin
- 오블완
- 퀄컴
- 공모주
- 파이썬
- 미국주식
- Apple
- Home Assistant
- 현대통신
- 홈네트워크
- 해외주식
- 애플
- Python
- 티스토리챌린지
- 월패드
- Espressif
- Today
- Total
YOGYUI
Python - 윈도OS 환경 변수 변경하기 (Modify Environment Variables) 본문
윈도 OS를 사용하다보면 (특히 개발자들은) 환경 변수를 설정해야하는 경우가 있다
제어판 - 시스템 - 고급 탭의 '환경 변수' GUI로 해결하는게 일반적이다
예시를 위해 'TEST' 변수를 한 개 만든 뒤에 Python으로 수정하는 방법을 알아보자
(이 글에서는 사용자 변수가 아니라 시스템 변수를 대상으로 한다)
임의로 C:\test1, C:\test2, C:\test3 3개의 경로를 값으로 입력했다
(실제 존재하지 않는 경로여도 상관없다)
여러 개의 경로를 입력할 경우 세미콜론(;) 구분자로 입력해줘야 한다
1. OS module
Python 구동 시 환경변수를 동적으로 변경하고자 할 경우 built-in 모듈인 os의 environ (_Environ) 클래스를 사용하는게 일반적인 방법이다
'TEST' 변수값을 가져오려면 딕셔너리를 사용하는 것과 동일하게 get 메서드를 사용하면 된다
(변수명을 딕셔너리의 key 값처럼 사용)
import os
test = os.environ.get('TEST') # 대소문자 구분 없음
# test = os.environ['TEST'] # 결과는 동일
In [1]: print(test)
Out[1]: C:\\test1;C:\\test2;C:\\test3
In [2]: type(test)
Out[2]: str
문자열 객체(str)로 반환되며, 경로 문자열로 구분하려면 다음과 같이 작성하면 된다
test_paths = test.split(';')
In [3]: print(test_paths)
Out[3]: ['C:\\test1', 'C:\\test2', 'C:\\test3']
C:\test4 경로를 하나 추가해보자
test_paths.append("C:\\test4")
os.environ['TEST'] = ';'.join(test_paths)
In [4]: os.environ['TEST']
Out[4]: C:\\test1;C:\\test2;C:\\test3;C:\\test4
아주 쉽게 추가할 수 있다
하지만!
제어판을 다시 열어보면 시스템 변수는 추가되지 않은 것을 알 수 있다
또한, 위 스크립트를 다시 실행해보면 C:\test4 경로는 처음에는 TEST 변수 내에 존재하지 않는다
즉, os 모듈의 environ으로는 시스템 변수를 영구적(permanently)으로 변경할 수 없으며, 스크립트가 돌아가는 환경 (IDE, Shell 등)이 구동될 때 로드된 일종의 '자식' 환경에서만 동적으로 변경이 되고 부모 환경은 접근할 수 없다
2. 레지스트리 변경하기
환경변수는 윈도우 레지스트리로 관리된다
레지스트리 편집기를 열어보자 (시작 - 실행 - regedit)
시스템 환경변수의 레지스트리 경로는 다음과 같다
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment
Python으로 윈도우 레지스트리에 접근하고자 할 때 가장 손쉬운 방법인 built-in 모듈 winreg를 사용해서 환경변수를 수정해보자
winreg의 자세한 사용법은 공식 도큐먼트 참고
https://docs.python.org/3/library/winreg.html
스크립트의 파일명은 registry.py로 지정
import winreg
name = 'HKEY_LOCAL_MACHINE'
path = r'SYSTEM\CurrentControlSet\Control\Session Manager\Environment'
tree = eval('winreg.%s'%name)
varname = 'TEST'
with winreg.ConnectRegistry(None, tree) as reg:
with winreg.OpenKey(reg, path, 0, winreg.KEY_ALL_ACCESS) as key:
value, type_id = winreg.QueryValueEx(key, varname)
print(value, type_id)
핵심: winreg의 QueryValueEx 함수로 특정 변수의 값을 쿼리
여기서 주의할 점은, 스크립트가 '관리자 권한'에서 실행되지 않으면 Access Denied 오류가 발생한다
(winreg 사용할 때 이게 제일 번거롭다)
python registry.py
>> PermissionError: [WinError 5] 액세스가 거부되었습니다
IDE나 프롬프트를 관리자 권한으로 실행하면 정상적으로 'TEST' 변수값을 읽을 수 있다
python registry.py
>> C:\test1;C:\test2;C:\test3 1
print 두번째 인자 '1'은 레지스트리 종류 중 하나인 문자열 값(REG_SZ)을 가리킨다
쿼리하고자 하는 변수명을 임의로 변경하기 위해 다음과 같이 함수로 만들어두자
def getSystemEnvironmentValue(varname: str):
name = 'HKEY_LOCAL_MACHINE'
path = r'SYSTEM\CurrentControlSet\Control\Session Manager\Environment'
tree = eval('winreg.%s'%name)
with winreg.ConnectRegistry(None, tree) as reg:
with winreg.OpenKey(reg, path, 0, winreg.KEY_ALL_ACCESS) as key:
try:
value, type_id = winreg.QueryValueEx(key, varname)
return value
except FileNotFoundError:
return '???'
값을 변경하는 방법도 읽는 방법과 유사하게 하면 된다
import winreg
import win32gui, win32con
name = 'HKEY_LOCAL_MACHINE'
path = r'SYSTEM\CurrentControlSet\Control\Session Manager\Environment'
tree = eval('winreg.%s'%name)
varname = 'TEST'
newpath = r'C:\test4'
originpath = getSystemEnvironmentValue(varname)
with winreg.ConnectRegistry(None, tree) as reg:
with winreg.OpenKey(reg, path, 0, winreg.KEY_ALL_ACCESS) as key:
to_write = originpath + ';' + newpath
winreg.SetValueEx(key, varname, 0, winreg.REG_EXPAND_SZ, to_write)
win32gui.SendMessage(win32con.HWND_BROADCAST, win32con.WM_SETTINGCHANGE, 0, 'Environment')
핵심: winreg의 SetValueEx 함수로 특정 변수의 값을 변경
또한, MFC의 SendMessage 함수로 실행중인 모든 프로세스에 환경변수가 변경되었음을 notify해주는 구문도 추가해주면 다른 프로세스에서도 해당 이벤트를 감지할 수 있다
위 스크립트를 실행 후 변수를 확인해보면
print(getSystemEnvironmentValue('TEST'))
>> C:\test1;C:\test2;C:\test3;C:\test4
동적으로 변경된 것을 확인할 수 있다
또한, 레지스트리 편집기와 제어판 환경변수에서도 값이 변경된 것을 확인할 수 있다
값을 쓰는 구문까지 같이 함수화해두자
import winreg
import win32gui, win32con
def getSystemEnvironmentValue(varname: str):
name = 'HKEY_LOCAL_MACHINE'
path = r'SYSTEM\CurrentControlSet\Control\Session Manager\Environment'
tree = eval('winreg.%s'%name)
with winreg.ConnectRegistry(None, tree) as reg:
with winreg.OpenKey(reg, path, 0, winreg.KEY_ALL_ACCESS) as key:
try:
value, type_id = winreg.QueryValueEx(key, varname)
return value
except FileNotFoundError:
return '???'
def setSystemEnvironmentValue(varname: str, value: str):
name = 'HKEY_LOCAL_MACHINE'
path = r'SYSTEM\CurrentControlSet\Control\Session Manager\Environment'
tree = eval('winreg.%s'%name)
with winreg.ConnectRegistry(None, tree) as reg:
with winreg.OpenKey(reg, path, 0, winreg.KEY_ALL_ACCESS) as key:
winreg.SetValueEx(key, varname, 0, winreg.REG_EXPAND_SZ, value)
win32gui.SendMessage(win32con.HWND_BROADCAST, win32con.WM_SETTINGCHANGE, 0, 'Environment')
시스템 환경변수는 특별한 경우가 아니라면 문자열 값을 가지는 게 정석이므로 값 속성은 REG_EXPAND_SZ로 두고 문자열을 쓸 수 있게 함수화했다
P.S)
- 변수 삭제는 winreg의 DeleteValue 함수를 사용하면 된다
- 유저 환경변수의 레지스트리 경로는 다음과 같다
HKEY_CURRENT_USER\Environment
유저 환경변수도 위에서 다룬 코드와 유사하게 name과 path만 변경하면 동일하게 동작한다
3. Extra
환경변수 중 'PATH'는 GUI를 통해 우선순위를 결정할 수 있다
우선순위를 Python으로 동적으로 변경하는 예도 알아보자
여기서는 위 환경변수에서 4번째 경로인 C:\Python38\Scripts와 5번째 경로인 C:\Python38\의 순서를 서로 바꿔보도록 한다
우선 위에서 구현한 함수를 사용해 'PATH' 값을 읽은 뒤 4번째, 5번째 경로를 확인해보자
path_list = getSystemEnvironmentValue('PATH').split(';')
print(path_list[3] + '\n' + path_list[4])
>>
C:\Python38\Scripts\
C:\Python38\
이제 리스트의 4번째 요소와 5번째 요소를 swap한 뒤, 다시 문자열로 만들어 'PATH'에 값을 입력해보자
path_list[3], path_list[4] = path_list[4], path_list[3]
to_write = ';'.join(path_list)
setSystemEnvironmentValue('PATH', to_write)
다시 환경변수의 PATH 항목을 살펴보면
4번째 항목과 5번째 항목이 서로 바뀐 것이 적용된 것을 확인할 수 있다
path_list = getSystemEnvironmentValue('PATH').split(';')
print(path_list[3] + '\n' + path_list[4])
>>
C:\Python38\
C:\Python38\Scripts\
경로 추가, 삭제, 변경도 위와 같이 값 문자열을 리스트로 치환한 뒤, 리스트 요소 조작 후 다시 세미콜론 구분 문자열로 만들어 레지스트리 값을 쓰는 방식으로 구현하면 된다
경로를 추가/삭제하는 함수를 예로 작성해보자
def addSysEnvPath(path: str):
origin = getSystemEnvironmentValue('PATH').split(';')
origin.append(path)
to_write = ';'.join(origin)
setSystemEnvironmentValue('PATH', to_write)
def removeSysEnvPath(path: str):
origin = getSystemEnvironmentValue('PATH').split(';')
if path in origin:
origin.remove(path)
to_write = ';'.join(origin)
setSystemEnvironmentValue('PATH', to_write)
4. 주의사항
레지스트리는 잘못 건드릴 경우 OS 동작에 치명적인 결함을 유발할 수 있으므로 사용함에 있어 각별한 주의가 요구된다
문제가 생겼을 경우 롤백할 수 있도록 레지스트리 값을 백업할 수 있게 구현해야 하며, 스크립트 및 함수는 엄격하게 제한된 인증을 거쳐서면 접근할 수 있도록 구현해야 한다
5. 기타
이 외에도 subprocess로 'SETX' 커맨드를 호출하기 혹은 배치 스크립트 작성 등 몇가지 방법들이 있는데, 그다지 pythonic하진 않기 때문에 관심있는 사람은 [참고]의 링크를 살펴보길 바란다
끝~!
[참고]
https://code.activestate.com/recipes/416087-persistent-environment-variables-on-windows/
'Software > Python' 카테고리의 다른 글
Python - 음의 정수를 16진수로 표현하기 (negative int to hex) (0) | 2021.08.24 |
---|---|
Python - list 요소 뒤집기 (reverse list elements) (0) | 2021.08.20 |
PyQt5 - Connect pyqtSignal in For loop (lambda problem) (0) | 2021.07.20 |
PyQt5 - QTextEdit Tab Width 설정하기 (0) | 2021.07.15 |
Python - Decorator in Class (0) | 2021.07.05 |