doscommand.pas(http://maxxdelphisite.free.fr/)を使用しているのですが、ActivePerl 5.10の標準入出力が上手く操作できません。
他のツールは大体上手くいくのですが、ActivePerlは特殊なのでしょうか?
アドバイスよろしくお願いします。
test.pl
------------
print "Start";
while(<>){
print "Loop";
}
print "End";
------------
unit1.pas
Button1で、test.pl実行
Button2で、perlの標準入力にEdit2の内容を送信
Button3で、ファイル終端記号送信
標準出力は、Memo1にされます。
DosCommand1のInputToOutputは、True。ReturnCodeは、rcCRLFに設定。
---------------------------
procedure TForm1.Button1Click(Sender: TObject);
begin
Memo1.Lines.Add('perl.exe test.pl');
DosCommand1.CommandLine := 'perl.exe test.pl';
DosCommand1.Execute;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
DosCommand1.SendLine(Edit2.Text, True);
end;
procedure TForm1.FormShow(Sender: TObject);
begin
DosCommand1.OutputLines := Memo1.Lines;
end;
procedure TForm1.Button3Click(Sender: TObject);
begin
DosCommand1.SendLine(#$1A, True);
end;
上手く行かないのはいいとして、何がどう上手くいかないんですか?
Perlとの標準入出力が上手くいかないのです。
test.plは、
1. Startを標準出力に出力
2. 改行つきの標準入力を受け取るたびに、Loopを標準出力に出力
3. ファイル終端記号を受け取ると、Endを出力
というプログラムですが、
標準入力、出力ともDelphi側とやり取りできない状態です。
ちなみに、
test.plを
print "Start";
のみにすると、標準出力のStartを受け取ることができます。
while以降の処理が入ると動作しなくなります。
当然ですが、コマンドプロンプトからは、test.plは正常に動作します。
Stdoutがフラッシュされてないって可能性ないですかね。
私はよくRubyを使いますが、標準出力をflushしないとcygwinのコンソールとかでも逐一表示されなかったりします。
flushの仕方はperlだと、
$| = 1;
を上の方に1つかいておいて、自動で出力時にflushさせるか、
参考:
日本語 perl texinfo - $vline
http://flex.ee.uec.ac.jp/texi/perl/perl_161.html#SEC226
書き出すたびに、明示的にflush関数を呼び出す、
flush('STDOUT');
でいけるんじゃないかな?
私はPerlは詳しくないので、
"Perl flush stdout"とかでgoogleさん検索してみてください、という感じです。
変ないい回しでした。
> Stdoutがフラッシュされてないって可能性ないですかね。
STDOUTがバッファリングされているのでflushしないといけない可能性はないですかね。
ということです。
TOBYさん、ご意見ありがとうございます。
僕もPerl詳しくないので、いただいた意見を参考に検討してみます。
ただ、標準入力も動作がおかしいと思っています。
Button3を押すと、Loopから抜けてperlのプログラムは終了するはずですが、終了しないので、入力値がPerlに届いていないのでは?
と思っています。
コンソールでテストコードを書いてみました。
標準入力や出力の受渡は正常にできてるみたいです。
(フラッシュしたらプログラム側で受け取れます)
ただ、標準入力で#$1A を渡してもループを抜けられませんね。
コマンドラインでも、ファイルに1Aをバイナリで記述して渡すと終了しません。
テキストモードだと行ける、という話がありますが、
binmode STDIN, ":crlf"
とかしても上手くいかず。
うーん。
とりあえず、Delphiは関係ない気がしますw
-------
program console_test;
{$APPTYPE CONSOLE}
uses
SysUtils, Windows, DosCommand in '..\DosCommand.pas';
type
TDosCommandTest = class
procedure OnNewLine(Sender: TObject; NewLine: string; OutputType: TOutputType);
function Main: TDosCommandTest;
end;
procedure TDosCommandTest.OnNewLine(Sender: TObject; NewLine: string; OutputType: TOutputType);
begin
if OutputType = otEntireLine then Writeln('Cmd: ', NewLine);
end;
function TDosCommandTest.Main(): TDosCommandTest;
var
i: Integer;
Cmd: TDosCommand;
begin
Cmd := TDosCommand.Create(nil);
try
Cmd.CommandLine := 'perl test.pl';
Cmd.InputToOutput := True;
Cmd.OnNewLine := OnNewLine;
Cmd.Execute;
// wait
while Cmd.IsRunning do begin
// ESCAPEで強制終了
if (GetAsyncKeyState(VK_ESCAPE) and $8000) <> 0 then break;
// Zでテスト出力
if (GetAsyncKeyState(Ord('Z')) and $8000) <> 0 then
Cmd.SendLine('test', True);
// Xで終了コード?を送る (うまくいかず)
if (GetAsyncKeyState(Ord('X')) and $8000) <> 0 then
Cmd.SendLine(#$1A, True);
Sleep(100);
end;
finally
FreeAndNil(Cmd);
end;
Writeln('DONE!');
Result := Self;
end;
begin
TDosCommandTest.Create.Main.Free;
end.
-----test.pl-----
$| = 1;
print "Start\n";
$i = 0;
while($a = <>){
print "Loop: $a $i\n";
$i++;
}
print "End\n";
TOBYさん、久世です。
サンプルコードありがとうございました。
何点か質問があります。
・TDosCommandのIsRunningっていうプロパティーは新規に追加されましたか?
・test.plの $|=1 がフラッシュと関係あるのでしょうか?
・IsRunningは、Activeに変更して私の方で試しましたが、標準出力が上手くされません。
ちなみに、OSはVistaですが、あまり関係と思っています。
すみません。
$l=1の説明は、前回教えてもらっていましたね。
IsRunningっていうプロパティーありますね。
僕の使用していたものより、新しいバージョンには入ってました。
失礼いたしました。
どこまで上手くいきましたか?
先ほどの私のコードでは、標準出力は問題なく取れています。
標準入力も問題ないです。
問題は、EOFが検出できないことです。
私のPerl(というか標準入出力周りの)理解力不足のせいもあります。
この件について、数日前からちょっと、2chのPerlスレで聞いてみてます。
(こっちの方が詳しいし早いと思ったので)
Perlについての質問箱 39箱目
http://pc11.2ch.net/test/read.cgi/tech/1234181856/311-
続いて質問してみているのですが、
たぶんに、while(<>)で検出ではなく、プログラムから標準入力を出す場合は、文字列内にEOFがあったら、というように検出しないといけないような気がします。(ファイルで渡した時と同様に)
TDosCommand.IsRunningプロパティは、
TDosCommand.Execute を呼び出すと、そのままスルーしてプログラムが実行されていくので、
(=Perlが終了するまで待ってくれない)
コンソールプログラムだとプログラムが終了してしまうのを防止するために
使っています。
TFormなどGUIですぐに終了しないなら必要ないかと。(逆に終了を待つ必要があるなら必要)
標準出力のうけとりはOnNewLineイベントを使っていますが、コードを見たところ、
OutputLinesプロパティでも問題なさそうですが…。
TOBYさん、いろいろ調べてもらってありがとうございます。
1. doscommand.zip
2. doscommand_tk.zip
の2種類のVCLがありますが、2だと標準入出力が上手くいきませんが、
1だと、TOBYさんと同じ状況になるのを確認しました。
1, 2でソースは大分差がありますが、どの部分がクリティカルに
利いているか悩んでいます。
また、ファイル終端が上手く検出できないのがまだ不思議でなりません。
コマンドプロンプトではCtrl+Zで上手くいっていますので・・・
ああ、すいません。使ったライブラリ書いておくべきでした。
使ったのはdoscommand.zipの方です。
doscommand_tk.zipは挙動が違うということなのかな…?
> また、ファイル終端が上手く検出できないのがまだ不思議でなりません。
これについては2chの方でお聞きしていたのですが、大体結論が出た感じです。
Perlについての質問箱 39箱目
http://pc11.2ch.net/test/read.cgi/tech/1234181856/352-353
> 352 デフォルトの名無しさん [sage] 2009/03/18(水) 20:17:40
> >>344
> だから 0x1a が EOF と思ってる時点で間違ってる
> まずここを理解しろよ
>
> CTRL-Z 押下は EOF を送るための特殊な操作であって、
> それが歴史的な理由で CTRL-Z (0x1a)になってるだけであって
> 0x1a を送れば EOF になるわけじゃない。
>
> 353 デフォルトの名無しさん [sage] 2009/03/19(木) 01:24:01
> >>344
> if (ナントカ eq "\x1a") { exit }
>
> こういう発想は思いつかんの?
ということらしいです。
コマンドプロンプトでのCtrl+Zと、プログラムで入力した0x1Aは別物、ということみたいです。
なので、0x1Aがあったら中断するように、自分でPerl側で処理しないといけない、ということになるのでしょう。
doscommand_tk.zipは挙動が違います。
また、このperlプログラムは、
標準入力を受け付けるので、ファイルからの入力も以下のようにいけます。
perl test.pl < input.txt
この場合は、間違いなく最後に入力されるのは
EOFだと思うのですが・・・
以上よろしくお願いします。
doscommand_tk.zipの方でやってみました。
Perl側。
#!/usr/local/bin/perl
$| = 1;
print "Start\n";
while(<>){
if ($_ eq "\x1A\n") { last; }
print $_;
}
print "End\n";
exit;
ループ中で\x1A\nを検出したらループを抜けるようします。
多分これで望みの結果が得られるのではないかなと。
ちなみにDosCommand1.SendLine(#$1A, True);だと\x1Aだけでなく改行も一緒に送ってるようなので\x1A\nで判定しています。
Perlは良く分からないのでこのあたりもっとうまく実装してみてください。
Dさんへ
検討ありがとうございます。
doscommand_tk.zipで上手くいきますか?
私のほうの挙動と違いますね。
Delphiの方のコードはどうしていますか?
while Cmd.IsRunning do begin
の、IsRunning プロパティは存在しないので、
Activeを使っているのですか?
>Delphiの方のコードはどうしていますか?
最初に久世さんが示されたコードのままと思います。
コンポーネントはインストールしてフォームに貼り付けました。
コンソールアプリではないので while〜 は使っていません。
DosCommand1のプロパティはInputToOutputはTrueにしましたがFalseでも動作に支障はありませんでした。
というかどのプロパティをいじっても動作に違いは出ますが支障はありませんでした。
Perl側で0x1Aがきたらループを抜けるという処理を足しただけです。
Delphi側。
unit main;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, DosCommand;
type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
Button3: TButton;
Edit2: TEdit;
Memo1: TMemo;
DosCommand1: TDosCommand;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure FormShow(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
Memo1.Lines.Add('perl.exe test.pl');
DosCommand1.CommandLine := 'perl.exe test.pl';
DosCommand1.Execute;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
DosCommand1.SendLine(Edit2.Text, True);
end;
procedure TForm1.Button3Click(Sender: TObject);
begin
DosCommand1.SendLine(#$1A, True);
end;
procedure TForm1.FormShow(Sender: TObject);
begin
DosCommand1.OutputLines := Memo1.Lines;
end;
end.
ちなみにPerlでは0x1Aを受け取ってもそれをファイルの終わりとみなすことはなく単なるデータとしての扱いにしかならないようです。
なのでループを抜ける条件は0x1Aに限らず、例えば0x0でも0x1でもあるいは"END"という文字列であっても、Delphi側とPerl側で申し合わせていれば何でもOKという感じです。
TOBYさんが2chで得た情報のとおりです。
またコンソールでCtrl+ZとしてもPerlに0x1Aは送られないようです。
#!/usr/local/bin/perl
$| = 1;
print "Start\n";
while(<>){
if (index($_, "\x1A") > -1) {
print "--- EOF ---\n";
last;
}
print $_;
}
print "End\n";
exit;
このようにした場合Button3を押して#$1Aを送った場合やテキストファイルの途中をバイナリエディタで0x1Aに書き換えたものをリダイレクトさせた場合"--- EOF ---"は出力されますがコンソールでCtrl+Zとした場合は出力されません。
素人考えなんですがコンソールからCtrl+Zとした場合0x1Aを送ってファイルの終わりとしているのではなく別のやり方でファイルの終わりとしているのではないかなと思いました。
TOBYさん、Dさん、ありがとうございました。
Perlの方を少し手直しする必要があるので、理想形ではないですが
目的は達成できるので、解決としたいと思います。
ツイート | ![]() |