LoginSignup
8
8

More than 5 years have passed since last update.

Pythonでシェルコマンドの実行結果をリストで渡す方法(ブロックしない版)

Last updated at Posted at 2016-06-22

Pythonでシェルコマンドの実行結果をリストで渡す方法 において、こんなソースがあった。これ、考え方はいいんだけど、シェルコマンドの終了を待つという点が惜しい。

res_cmd_no_lfeed.py
#!/usr/bin/python

import subprocess

def res_cmd_lfeed(cmd):
  return subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True).stdout.readlines()

def res_cmd_no_lfeed(cmd):
  return [str(x).rstrip("\n") for x in res_cmd_lfeed(cmd)]

def main():
  cmd = ("ls -l")
  print(res_cmd_no_lfeed(cmd))

if __name__ == '__main__':
  main()

そこで、yield を使う

res_cmd_no_lfeed.py
#!/usr/bin/python

import subprocess

def res_cmd_lfeed(cmd):  # 戻り値はリストではなく各行
  for line in subprocess.Popen(cmd, stdout=subprocess.PIPE,shell=True).stdout:
      yield line

def res_cmd_no_lfeed(cmd):
  return [str(x).rstrip("\n") for x in res_cmd_lfeed(cmd)]

def main():
  cmd = ("ls -l")
  print(res_cmd_no_lfeed(cmd))

if __name__ == '__main__':
  main()

res_cmd_lfeed はコマンド終了までずっとブロックしてしまうのではなく、ループの中で1行受け取ったら yield 、つまりそのループをいったん停止し、そのときの結果を返す。
「次よこせ」と言われたら再開して、次の結果を返す……というように動作する。

この例だと結局は res_cmd_no_lfeed で Arrayが全部完成するまで待つので意味ないんだけどね。
Webアプリだったら、どんどんブラウザに応答を返しちゃうとか、できる。

なにがうれしいのか

  • シェルコマンドが無限に出力を生成するような場合でも、その途中までの結果を受け取れる。
  • シェルコマンドの実行に時間がかかる場合でも、途中までの結果を早期に受け取れる。
  • 一度に作業する対象が小さいので、ワーキングメモリが最小限で済む。
  • 後半で失敗したとしても、途中までの結果を返しちゃってるので、取り返しがつかない。うれしくない
forevertime.bat
@echo off
:TOP
echo %time%
goto top

こんなのを実行してみるとわかる。このバッチファイルは無限に出力しつづけるので、前者のソースだと readlines() で待ち続けてしまうが、後者は結果をどんどん取り込んで処理する。「ちぎっては投げ」のように。

注意。パイプにはバッファがある

  • シェルコマンドの結果を受け取ってやらないと、シェルコマンド側が出力が詰まってしまって停まることがある。Windows10で試したところ、12kb程度のバッファが存在している模様。受け取るほうがのんびりしてると、タイムスタンプが10秒飛ぶ――みたいな現象として観測できる。
forevertime.bat(大量出力版)
@echo off
:TOP
set /P X=0123456789012345678901234567890123456789012345678901234567890123<NUL
set /P X=0123456789012345678901234567890123456789012345678901234567890123<NUL
set /P X=0123456789012345678901234567890123456789012345678901234567890123<NUL
set /P X=0123456789012345678901234567890123456789012345678901234567890123<NUL
set /P X=0123456789012345678901234567890123456789012345678901234567890123<NUL
set /P X=0123456789012345678901234567890123456789012345678901234567890123<NUL
set /P X=0123456789012345678901234567890123456789012345678901234567890123<NUL
set /P X=0123456789012345678901234567890123456789012345678901234567890123<NUL
echo %time%
goto top

もっともっとチューニングできそうだけれど、まあ、このへんにしておくか。

8
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
8