#!/usr/bin/perl -t

###################################################################
#  任意のコマンドの実行時間を利用しやすい形で取り出すための、コマンド
#   - 任意の実行可能なコマンドの、直前に このプログラムを追加して使う。
#   - マイクロ秒単位で出力する。小数点以下6桁の秒単位で出力するには、-6 をオブションに書く。
#  作成者 : 下野寿之 Shimono Toshiyuki (2016-05-17)
###########################################################

use v5.8 ; use strict ; use warnings ; # warnings は 5.6~ 
use Time::HiRes qw [ gettimeofday tv_interval ] ; # 5.7~
use Getopt::Std ; getopts '016qm:t^:!' , \my%o ; # 5~ 
use Term::ANSIColor qw[ :constants color ] ; $Term::ANSIColor::AUTORESET = 0 ; # 5.6 ~ 
use FindBin qw[ $Script ] ; # 5.3 ~ 

do { select STDERR ; HELP_MESSAGE () } if ! @ARGV ; 

sub main ( ) ; 
sub provide ( $ ) ; 
$| = 1 if $o{'!'} ;
$o{m} //= 1 ; 

main () ; 
exit 0 ;

sub main ( ) { 

  $SIG{INT} = sub {print STDERR CYAN "Did you try stopping this program? Push Ctrl+\\ keys to terminate this `$Script'\n"} ;

  for ( 1 .. $o{m} ) { 
    my $t0 = [ gettimeofday ] ; # 開始時刻の記録

    # 指定されたコマンドの実行
    no warnings ; 
    if ( @ARGV ) { my $t = qx [@ARGV] ; print $t unless $o{0}} else { while ( <> ) { 1 } }  # <-- SIG{HUP} で停止できる。
    my $td = tv_interval $t0 ; # 時刻差の取得 # 正確さ優先のため、この行は単純なコマンド文にとどめる。
    provide ( $td ) ;
  }
}

sub provide ( $ ) { 
  # 時刻差の表示
  my $td = $_[0] ; # 秒数 ( 100万倍すると、マイクロ秒になる。)
  my $FH = $o{1} ? *STDOUT : *STDERR ; 
  print {$FH} $o{q} ? '' : color ( 'bright_green' ) unless $o{1} ; 
  print {$FH} eval qq[qq[$o{'^'}]] if defined $o{'^'} ;  # 先頭に付加する文字の表示
  print {$FH} sprintf('%0.0f',$td*1e6) if ! $o{6} ; # マイクロ秒単位で整数を表示。
  print {$FH} sprintf('%0.6f',$td    ) if   $o{6} ; # 小数点以下6桁で表示。
  print {$FH} "\t", sprintf('%02d:%02d:%02d',@{[localtime]}[2,1,0])  if $o{t} ; 
  print {$FH} color ( 'reset' ) if ! $o{1} || $o{q} ; 
  print {$FH} "\n" ;
}

sub VERSION_MESSAGE {}
sub HELP_MESSAGE {
    use FindBin qw[ $Script ] ; 
    my ($a1,$L,$opt,@out) = ($ARGV[1]//'',0,'^o(p(t(i(o(ns?)?)?)?)?)?$') ;
    open my $FH , '<' , $0 ;
    while(<$FH>){
        s/\$0/$Script/g ;
        $out[$L] .= $_ if s/^=head1\s*(.*)\n/$1/s .. s/^=cut\n//s && ++$L and $a1 =~ /$opt/i ? m/^\s+\-/ : 1 ;
    }
    close $FH ;
    print $ENV{LANG} =~ m/^ja/ ? $out[0] : $out[1] // $out[0] ;
    exit 0 ;
}


=encoding utf8

=head1 

  $0 コマンド文字列
  $0 -6 コマンド文字列

   コマンド文字列で指定されたコマンドの実行時間を計測し、
   マイクロ秒単位または秒単位で表示する。

  commands | $0 cat
  commands | $0 -6 cat
    上記と同様。

オプション: 

   -0 : コマンド文字列の標準出力を捨てる。
   -1 : 測定した時間を標準エラー出力ではなくて、標準出力に出力する。
   -6 : 測定した時間をマイクロ秒単位ではなくて、秒単位で表示する。
   -q : 標準エラー出力に、実行時間を出力する際に色を出さない。
   -m num : 反復回数の指定。 
   -t : 終了時の時刻も表示する。

   -^ str : 測定情報の先頭に付加する文字列の指定。
   -! : 出力をバッファに貯めない。

使用例: 

   $0 perl -e 'sleep 1'
   $0 -16 -^ "Time Elapsed (sec) = " echo Hello! 
   $0 -16 -^ "\n" printf "abcde"

開発メモ: 

   * ごく簡単なコマンドの演算時間は2ミリ秒前後である。
   * コマンドの実行に system でも qx でもこの値に大きな変わりは無かった。ただし systemが動いているときは $0 が Ctrl+CでもCtrl+\でも止まらない。
   * 5.001 でも 5.018 でも演算時間に変わりは見いだせなかった。 

=cut


