次のbashコマンドパターンでmemcachedを実行しています。
memcached -vv 2>&1 | tee memkeywatch2010098.log 2>&1 | ~/bin/memtracer.py | tee memkeywatchCounts20100908.log
プラットフォーム全体のキーのセットへの比類のない取得を追跡しようとします。
Memtracerスクリプトは以下のとおりであり、1つの小さな問題がありますが、希望どおりに機能します。中間ログファイルのサイズを監視していると、memkeywatchYMD.logのサイズが約15〜18Kになるまで、memtracer.pyは入力の取得を開始しません。 stdinで読み取るより良い方法、または応答時間を短縮するためにバッファサイズを1k未満に減らす方法はありますか?
#!/usr/bin/python
import sys
from collections import defaultdict
if __name__ == "__main__":
keys = defaultdict(int)
GET = 1
SET = 2
CLIENT = 1
SERVER = 2
#if <
for line in sys.stdin:
key = None
components = line.strip().split(" ")
#newConn = components[0][1:3]
direction = CLIENT if components[0].startswith("<") else SERVER
#if lastConn != newConn:
# lastConn = newConn
if direction == CLIENT:
command = SET if components[1] == "set" else GET
key = components[2]
if command == SET:
keys[key] -= 1
Elif direction == SERVER:
command = components[1]
if command == "sending":
key = components[3]
keys[key] += 1
if key != None:
print "%s:%s" % ( key, keys[key], )
Pythonの-u
フラグを使用すると、stdin/stdoutからバッファリングを完全に削除できます。
-u : unbuffered binary stdout and stderr (also PYTHONUNBUFFERED=x)
see man page for details on internal buffering relating to '-u'
そしてmanページは明確にします:
-u Force stdin, stdout and stderr to be totally unbuffered. On
systems where it matters, also put stdin, stdout and stderr in
binary mode. Note that there is internal buffering in xread-
lines(), readlines() and file-object iterators ("for line in
sys.stdin") which is not influenced by this option. To work
around this, you will want to use "sys.stdin.readline()" inside
a "while 1:" loop.
これを超えると、既存のファイルのバッファリングを変更することはサポートされませんが、既存のファイル記述子と同じ基になるファイル記述子を使用して新しいファイルオブジェクトを作成できますできます 、および場合によっては異なるバッファリング、 os.fdopen を使用します。つまり、
import os
import sys
newin = os.fdopen(sys.stdin.fileno(), 'r', 100)
shouldnewin
を、標準入力と同じFDを読み取るファイルオブジェクトの名前にバインドしますが、バッファリングされるのは約100バイトのみです。時間(そして、sys.stdin = newin
を続行して、そこから新しいファイルオブジェクトを標準入力として使用できます)。この領域がを使用して、一部のプラットフォームで多くのバグや問題が発生したため、「すべき」と言います(クロスプラットフォームに完全な機能を提供するのはかなり難しい機能です)一般性)-現在の状態はわかりませんが、すべてがスムーズに進むように、対象となるすべてのプラットフォームで徹底的にテストすることを強くお勧めします。 (-u
、バッファリングを完全に削除すると、要件を満たす可能性がある場合は、すべてのプラットフォームで問題が少なくなります)。
sys.stdin.readline()
の代わりにsys.stdin.__iter__()
を使用するだけです。
import sys
while True:
line = sys.stdin.readline()
if not line: break # EOF
sys.stdout.write('> ' + line.upper())
これにより、Ubuntu13.04でPython 2.7.4およびPython 3.3.1を使用した行バッファー読み取りが可能になります。
sys.stdin.__iter__
はまだ行バッファリングされているので、 iter
の2引数形式 を使用して、ほぼ同じように動作するイテレータ(EOFで停止しますが、stdin.__iter__
は停止しません)を使用できます。 sys.stdin.readline
のイテレータを作成します。
import sys
for line in iter(sys.stdin.readline, ''):
sys.stdout.write('> ' + line.upper())
または、センチネルとしてNone
を指定します(ただし、EOF条件を自分で処理する必要があることに注意してください)。
これはPython 3.4.3:
_import os
import sys
unbuffered_stdin = os.fdopen(sys.stdin.fileno(), 'rb', buffering=0)
_
fdopen()
のドキュメントには、open()
の単なるエイリアスであると記載されています。
open()
にはオプションのbuffering
パラメータがあります:
bufferingは、バッファリングポリシーを設定するために使用されるオプションの整数です。 0を渡してバッファリングをオフに切り替え(バイナリモードでのみ許可)、1を渡して行バッファリングを選択し(テキストモードでのみ使用可能)、1より大きい整数を渡して固定サイズのチャンクバッファのサイズをバイト単位で示します。
言い換えると:
問題はPythonではなく、コマンドをパイプでチェーンするときにLinuxシェルが注入するバッファリングにある可能性があります。これが問題である場合、入力は行ごとではなく、 4Kブロック。
このバッファリングを停止するには、コマンドチェーンの前にunbuffer
パッケージのexpect
コマンドを付けます(例:
unbuffer memcached -vv 2>&1 | unbuffer -p tee memkeywatch2010098.log 2>&1 | unbuffer -p ~/bin/memtracer.py | tee memkeywatchCounts20100908.log
パイプラインの途中で使用する場合、unbuffer
コマンドには-p
オプションが必要です。
python 2.7でそれを行うことができる唯一の方法は:
_tty.setcbreak(sys.stdin.fileno())
_
from Pythonノンブロッキングコンソール入力 。これにより、バッファリングが完全に無効になり、エコーも抑制されます。
編集:アレックスの答えに関して、私の場合、最初の命題(python with _-u
_)を呼び出すことはできません( シバン制限 を参照)。
2番目の命題(小さいバッファーでfdを複製する:os.fdopen(sys.stdin.fileno(), 'r', 100)
)は、0または1のバッファーを使用すると機能しません。これは、対話型入力の場合であり、押されたすべての文字をすぐに処理する必要があるためです。