Windows環境のPythonにおいてsubprocess
を実行するとコマンドを実行できる
しかし, 次の問題が発生する
- PCの設定によって文字エンコーディングが変わること
- PCの言語環境によって返ってくる言葉が変わること
以上のことに対処した
先に結論
- 現在の文字エンコーディングは
locale.getpreferredencoding()
でわかる - 言語環境は
subprocess.run(['chcp.com', '437'])
で変更できる
文字エンコーディングの対処
Pythonのsubprocessを使って入手できる出力はデコードする必要がある
subprocess.run(
['ipconfig'], check=True, stdout=subprocess.PIPE,).stdout
"""
\r\nWindows IP \x8d\\\x90\xac\r\n\r\n\r\n\x83C\x81[\x83T\x83l\x83b\x83g...
"""
日本語の環境では cp932 (shift-JIS) でデコードすればよい
subprocess.run(
['ipconfig'], check=True, stdout=subprocess.PIPE,)\
.stdout.decode('cp932')
"""
\r\nWindows IP 構成\r\n\r\n\r\nイーサネット アダプター イーサネット:...
"""
しかし, 文字コードは言語環境が変われば変わってしまうため, ハードコーディングはしたくない
調べてるとchcpを使ってコードページを変更する例がでてくる
しかし、Pythonのsubprocess
からは文字コードを変えることはできなかった。
subprocess.run(
['chcp.com', '65001'], check=True, stdout=subprocess.PIPE,).stdout
"""
Active code page: 65001\r\n
"""
subprocess.run(
['ipconfig'], check=True, stdout=subprocess.PIPE,).stdout
"""
\r\nWindows IP Configuration\r\n\r\n\r\nEthernet adapter \x83C\x81[\x83T\x83l\x83b\x83g: ...
"""
文字コードの確定
locale
モジュールの getpreferredencoding
関数を用いることで環境の文字コードを取得可能である
locale.getpreferredencoding()
"""cp932"""
subprocess.run(
['ipconfig'], check=True, stdout=subprocess.PIPE,).stdout.decode(
locale.getpreferredencoding())
"""
\r\nWindows IP 構成\r\n\r\n\r\nイーサネット アダプター イーサネット:...
"""
言語環境の設定
言語環境によって表示されるキーワードが変わる.
これは特定の情報を入手したいときにキー名が環境で代わり不便である.
p = subprocess.run(
['ipconfig'], check=True, stdout=subprocess.PIPE,)
data = p.stdout.decode(locale.getpreferredencoding()).split('\r\n')
for line in data:
print(line)
"""
Japanese環境
----------
Windows IP 構成
イーサネット アダプター イーサネット:
メディアの状態. . . . . . . . . . . .: メディアは接続されていません
接続固有の DNS サフィックス . . . . .:
...
US環境
----------
Windows IP Configuration
Ethernet adapter イーサネット:
Media State . . . . . . . . . . . : Media disconnected
Connection-specific DNS Suffix . :
...
"""
そこで chcp
コマンドによって言語環境を変更しUS環境で情報を取得する
(ただし上のように「Ethernet adapter イーサネット:」のような日本語文字が残ってしまうようだ)
https://learn.microsoft.com/ja-jp/windows-server/administration/windows-commands/chcp
コンテキストマネージャを使いこのように書ける
@contextmanager
def code_page(code_page: int):
encoding = locale.getpreferredencoding()
try:
p = subprocess.run(
['chcp.com'], check=True,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
init_cp = p.stdout.decode(encoding=encoding).replace('\r\n', '')\
.split(' ')[-1]
subprocess.run(
['chcp.com', str(code_page)], check=True,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
yield
finally:
subprocess.run(
['chcp.com', init_cp], check=True,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
with code_page(437):
d = subprocess.run(
['ipconfig'], check=True, stdout=subprocess.PIPE,)
data = d.stdout.decode(locale.getpreferredencoding()).split('\r\n')
with 文を使いコードページを臨時で変更することができる。
おわり
コード全体
from contextlib import contextmanager
import subprocess
import locale
# %%
@contextmanager
def code_page(code_page: int):
encoding = locale.getpreferredencoding()
try:
p = subprocess.run(
['chcp.com'], check=True,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
init_cp = p.stdout.decode(encoding=encoding).replace('\r\n', '')\
.split(' ')[-1]
subprocess.run(
['chcp.com', str(code_page)], check=True,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
yield
finally:
subprocess.run(
['chcp.com', init_cp], check=True,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
# %%
def get_ipconfig():
with code_page(437):
d = subprocess.run(
['ipconfig'], check=True, stdout=subprocess.PIPE,)
return d.stdout.decode(locale.getpreferredencoding()).split('\r\n')
# %%
data = get_ipconfig()
for line in data:
print(line)
# %%
以上