読者です 読者をやめる 読者になる 読者になる

続 カッコの付け方

AWSを始めとしたクラウドコンピューティング全般と、唯一神emacsにおける()の付け方についてだらだら書きます

perl/python/rubyなど、スクリプト実行中の標準出力を、リアルタイムでリダイレクトできないとき

Linux Python Perl ruby

突然ですが、このコードを走らせている途中で in.txtを tail -f とかしたらどうなるでしょう?

$ cat loop.pl
#!/usr/bin/perl
use strict;

my $i = 0;
while(1) {
  $i++;
  print STDOUT "hello".$i."\n";
  print STDERR "hello-error".$i."\n";
  sleep(1);
}
$ ./loop.pl > in.txt 2> err.txt

上記を一つのターミナルで走らせて(bg実行でもいいです)、他のターミナルで tail -f in.txt やったら? 何も表示されない。。。 となります。一方 tail -f err.txtは書き込まれます。なぜでしょう?

perlpython,ruby,PHPは、デフォルトで標準出力をバッファリングしてます。つまりスクリプトの実行が完了するまで、ファイルにフラッシュ出来ません。画面=標準出力に逐一出ているので、納得できませんが、そういうことです。

一方、標準エラーは即時フラッシュしているので、err.txtに書かれるというわけです。 じゃあ、この標準出力のバッファリングを黙らせてやればよいです。

perlの場合

#!/usr/bin/perl
use strict;

#stop buffering
local $| = 1;

my $i = 0;
while(1) {
  $i++;
  print STDOUT "hello".$i."\n";
  print STDERR "hello-error".$i."\n";
  sleep(1);
}

pythonの場合

#!/usr/bin/python

import time
import sys
import os

# stop buffering
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)

i = 0
while 1:
    i = i + 1
    print u"hello%d" % i
    sys.stderr.write(u"hello-error%d\n" % i)
    time.sleep(1)

rubyの場合

#!/usr/bin/ruby

# stop buffering
STDOUT.sync = true

i = 0
loop {
  i += 1
  print "hello" + i.to_s() + "\n"
  STDERR.print "hello-error" + i.to_s() + "\n"
  sleep(1);
}

やっぱrubyええわ~ 分かりやすい。PHPは自分で調べてね!(PHPCLIを作る気がしないのと、emacsのflycheck-phpが非力で頼れないので。) スクリプト言語で長時間走る処理の、標準出力を読みたい!終わった後じゃなく途中を!という場合、ちゃんとファイルを開いてログに書けばいいのですが、リダイレクトで何とかしたい、ときにハマりましたので、備忘録として書いておきます。