ベスパリブ

プログラミングを主とした日記・備忘録です。ベスパ持ってないです。

Python3でラズパイのIPアドレスを取得する

python上でifconfigコマンドを実行し、返ってきた結果にIPアドレスがあればそれを表示することを考えます。
"LANG=C ifconfig"とするとコマンド結果が全て英語で返ってきて、処理しやすくなります。

import subprocess
subprocess.call(("LANG=C", "ifconfig"))  # エラー起きる

残念ながら、上記の方法だとエラーが発生します。"LANG=C"を使うときはcallではなくてPopenを使うと良いそうです。
Popenは公式によると、

17.5.1.2. Popen コンストラクター(原文) このモジュールの中で、根底のプロセス生成と管理は Popen クラスによって扱われます。簡易関数によってカバーされないあまり一般的でないケースを開発者が扱えるように、Popen クラスは多くの柔軟性を提供しています。 class subprocess.Popen(args, bufsize=-1, executable=None, stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=True, shell=False, cwd=None, env=None, universal_newlines=False, startupinfo=None, creationflags=0, restore_signals=True, start_new_session=False, pass_fds=(), *, encoding=None, errors=None)
(中略)
注釈 子プロセスのために環境を変更する必要がある場合は、preexec_fn の中でそれをするのではなく env 引数を使用します。start_new_session 引数は、子プロセスの中で os.setsid() を呼ぶ過去の一般的な preexec_fn の使用方法の代わりになります。

(オプション引数多すぎ!)
結論から言うと、env引数で指定すれば良いです。LANG=Cは環境変更のコマンド(?)になるので、subprocess.callでifconfigと併用できなかったようです。

p = subprocess.Popen(
  "ifconfig",
  #stdin=subprocess.PIPE
  stdout=subprocess.PIPE,
  stderr=subprocess.PIPE, 
  env={'LANG':'C'},
  shell=True
)
out, err = p.communicate()
print(out)

shell引数をTrueにしておかないと、"ifconfig"コマンドを認識してくれません。
stdout, stderr引数がないと、outとerrを受け取ることが出来ません。
結果としてoutは以下のようなものを得られます。

b'eth0      Link encap:Ethernet  HWaddr b8:27:eb:xx:xx:xx  \n          UP BROADC
AST MULTICAST  MTU:1500  Metric:1\n          RX packets:4431131 errors:0 dropped
:9222 overruns:0 frame:0\n          TX packets:18104015 errors:0 dropped:0 overr
uns:0 carrier:0\n          collisions:0 txqueuelen:1000 \n          RX bytes:122
0313021 (1.1 GiB)  TX bytes:3488088120 (3.2 GiB)\n\nlo        Link encap:Local L
oopback  \n          inet addr:127.0.0.1  Mask:255.0.0.0\n          inet6 addr:
::1/128 Scope:Host\n          UP LOOPBACK RUNNING  MTU:65536  Metric:1\n
  RX packets:31487788 errors:0 dropped:0 overruns:0 frame:0\n          TX packet
s:31487788 errors:0 dropped:0 overruns:0 carrier:0\n          collisions:0 txque
uelen:1 \n          RX bytes:29619310733 (27.5 GiB)  TX bytes:29619310733 (27.5
GiB)\n\nwlan0     Link encap:Ethernet  HWaddr b8:27:eb:xx:xx:xx  \n          ine
t addr:192.168.5.129  Bcast:192.168.5.255  Mask:255.255.255.0\n          inet6 a
ddr: fe80::9e6e:73a1:2f3a:e0b1/64 Scope:Link\n          UP BROADCAST RUNNING MUL
TICAST  MTU:1500  Metric:1\n          RX packets:19745725 errors:0 dropped:30867
91 overruns:0 frame:0\n          TX packets:1280736 errors:0 dropped:0 overruns:
0 carrier:0\n          collisions:0 txqueuelen:1000 \n          RX bytes:1023118
1 (9.7 MiB)  TX bytes:1462394071 (1.3 GiB)\n\n'

結果はバイナリで返ってきていますので、適当にdecodeして文字列に直してから処理をします。

  str_out = out.decode("ascii", "ignore")
  str_out_lines = str_out.splitlines()
  ip_addr = {"eth0":"", "wlan0":"", "lo":"", "":""}
  now_if  = ""
  for line in str_out_lines:
    line = line.lower()
    if line.startswith("eth0"):
      now_if = "eth0"
      continue
    elif line.startswith("lo"):
      now_if = "lo"
      continue
    elif line.startswith("wlan0"):
      now_if = "wlan0"
      continue
    if "inet addr:" in line:
      blocks = line.split()
      # blocks[1] = "addr:xxx.xxx.xxx.xxx"
      ip_addr[now_if] = blocks[1][5:]

    # 確認のため標準出力する
    for key in ip_addr:
      print("ip_addr[{}] = {}".format(key, ip_addr[key]))

標準出力の結果は以下のようになります。

ip_addr[lo] = 127.0.0.1
ip_addr[] =
ip_addr[eth0] = 192.168.5.128
ip_addr[wlan0] = 192.168.5.129

eth0が有線LAN、wlan0が無線LANIPアドレスです。