bart has asked for the wisdom of the Perl Monks concerning the following question:
I'd like to use the standard DOS box, that perl displays when running a perl script on my Windows box, for my normal output, and I'd like to add another DOS box, for my STDERR, for info/error messages, without interfering with my normal output.
I didn't find anything on CPAN, the docs of Win32::Console say:
A process cannot be associated with more than one console, so this method will fail if there is already an allocated console.
Perhaps somebody here has done this before? If so, can you share your recipe?
Re: Open a second DOS window
by Thelonius (Priest) on Oct 08, 2003 at 18:29 UTC
|
There probably isn't a way to do it with one process. However, you can use one process to display whatever is sent through a socket (or named pipe ...) and just connect STDERR to the socket. E.g. here's the program to show the output: #!perl -w
# usage: showerr.pl port
use IO::Socket;
use strict;
$| = 1;
$SIG{CHLD} = 'IGNORE';
my $localport = shift;
my $socklisten = IO::Socket::INET->new(LocalPort => $localport,
Listen => 2,
Reuse => 1,
Proto => 'tcp')
or die "Cannot open sock on $localport: $!\n";
while (my $readport = $socklisten->accept) {
print "Accepted new connection at ", scalar(localtime), "\n";
print while <$readport>;
print "Connection closed at ", scalar(localtime), "\n";
}
You can start that program independently or you could have the main program start it as below (in which case you would want to take out the outer while loop (replace while with if)):#!perl -w
use IO::Socket;
use strict;
my $errsocket = "1999";
system("start showerr.pl 1999");
sleep(5);
*STDERR = IO::Socket::INET->new(
Proto => "tcp",
PeerAddr => "localhost:$errsocket",
Timeout => 30,
)
or die "cannot create socketremote: $!\n";
warn "This is a test";
print STDERR "testing\n";
sleep(10);
Note that there's an awkward sleep() here to wait an undetermined time for the listener to start. This race condition could be fixed, but it might be simpler to just run the showerr program independently first.
| [reply] [Watch: Dir/Any] [d/l] [select] |
|
Here's a better version of my program above, combined into one program and with the race condition fixed:
#!perl -w
use Socket;
use IO::Socket;
use Getopt::Std;
use strict;
$| = 1;
my %opts;
getopts("e:", \%opts);
if ($opts{e}) {
showerrs($opts{e});
}
my $socklisten = IO::Socket::INET->new(LocalPort => 0,
Listen => 2,
Reuse => 1,
Proto => 'tcp')
or die "Cannot open socket: $!\n";
my ($port, $myaddr) = sockaddr_in(getsockname($socklisten));
print "port = $port\n";
# Here we start the error process
system("start perl $0 -e $port\n");
# should put alarm() timeout around this section
if (my $readport = $socklisten->accept) {
print "reading from port\n";
chomp(my $portback = <$readport>);
print "Connecting to $portback\n";
close($readport);
*STDERR = IO::Socket::INET->new(
Proto => "tcp",
PeerAddr => "localhost:$portback",
Timeout => 30,
)
or die "cannot create socketremote: $!\n";
}
close($socklisten);
#
warn "this is a test";
sleep(3);
print "program exiting\n";
die "I died!!!\n";
sub showerrs {
my $port1 = shift;
my $socklisten = IO::Socket::INET->new(LocalPort => 0,
Listen => 2,
Reuse => 1,
Proto => 'tcp')
or die "Cannot open socket: $!\n";
my ($port, $myaddr) = sockaddr_in(getsockname($socklisten));
my $out = IO::Socket::INET->new(
Proto => "tcp",
PeerAddr => "localhost:$port1",
Timeout => 30,
)
or die "cannot create socketremote: $!\n";
print $out "$port\n";
close($out);
if (my $readport = $socklisten->accept) {
print while <$readport>;
}
print "Program terminated\n";
# Unless your DOS box is already configured to wait:
print "Press Enter to exit ..."; $_ = <>;
exit(0);
}
| [reply] [Watch: Dir/Any] [d/l] |
Re: Open a second DOS window
by benn (Vicar) on Oct 08, 2003 at 18:36 UTC
|
I don't think this is possible. You could maybe try redirecting STDERR to a file, and typeing it from another console window though, using something like...
perl myscript.pl 2>stderr.txt
...though this only works on later Windows versions - 2000 upwards, I believe. For earlier versions, you could try one of the various DOS redirect utilities available.
HTH, Ben. | [reply] [Watch: Dir/Any] [d/l] [select] |
Re: Open a second DOS window
by castaway (Parson) on Oct 08, 2003 at 18:41 UTC
|
| [reply] [Watch: Dir/Any] |
|
Hi,
why dont you use perl:TK - follow the link
http://www.perlmonks.com/index.pl?node_id=297848
The question there was how to create a tail fkt with Tk. Solution is very straight forward. If you like the 2nd dos box because of the copy/past capability some widgets like Text will do the same job. Dont start to hassle with socket programming - if you do that properly you will end with a log-server ....
Cheers
Hartwig
PS. Can you explain in more detail why there is a need for a 2nd dos-box?
| [reply] [Watch: Dir/Any] |
Re: Open a second DOS window
by vek (Prior) on Oct 09, 2003 at 15:03 UTC
|
I agree with some of the other suggestions that logging to a file is probably a better way to go. Redirecting STDERR would be a good start, then as you progress further and want to log all kinds of things (as I'm sure you will in time) try Log::Log4perl. I've started using it instead of our home grown logging system and I like it a lot. Of course it might be total overkill for what you're attempting here though :-)
| [reply] [Watch: Dir/Any] |
Re: Open a second DOS window (a working possibility)
by bart (Canon) on Oct 09, 2003 at 22:35 UTC
|
Playing around with all the options I have, I tried launching a second copy of perl, using the open HANDLE, "| program" syntax. Just piping to perl makes its own output appear in the same console as that of the parent program, but by adding "start" to the chain, it works as I'd like! It opens a second console window, that prints the messages it gets (after tweaking them a little , just to show it does something), and that stays open as long as the first program runs. It has successfully been tested on Win98.
One problem I see is with the console window title bars. The second console gets a nice title "perl", but the original program gets a pretty ugly title, consisting of the whole command line bar the pipe symbol, of the program it just started. It actually appears on the wrong console window. Oh well.
Now it's just a matter of building a tiny module that does whatever needs to be done. In the meantime, you can play with this:
#!perl -wl
open FOO, qq[| start "$^X" -lne ] . q["print qq['$_' OK]"];
my $stdout = select FOO; $| = 1; select $stdout;
print FOO for qw(one two three);
sleep 10;
print "Finished";
Update (24 hours later): Oh, damn. Though it works well on Win98, today I've had a chance to try and run it on XP, and it doesn't work there, at all. Perhaps it's a difference between the start executables on both platforms, or else, between the command line interpreters — command.exe vs. cmd.exe.
I'll have to try and use BrowserUK's version, using Windows::Console and avoiding start completely. It works on Win98, and hopefully, it'll work on XP, too. And onb anything in between. | [reply] [Watch: Dir/Any] [d/l] |
|
#! perl -plw
use strict;
use Win32::Console;
BEGIN{
my $c=new Win32::Console( STD_ERROR_HANDLE );
$c->Free();
$c->Alloc();
}
END{
print 'Paused'; 1 while sleep 1;
}
Name that as Errcon.pl and then do
perl -e"open F, '| perl ErrCon.pl' or die $!; print F 'Hello world', $
+_, $/ for 1 .. 100"
You'll need to use ^Break to end the progam.
Examine what is said, not who speaks.
"Efficiency is intelligent laziness." -David Dunham
"Think for yourself!" - Abigail
| [reply] [Watch: Dir/Any] [d/l] [select] |
Re: Open a second DOS window
by jonadab (Parson) on Oct 09, 2003 at 19:37 UTC
|
It is possible to accomplish what you want, but not
straightforwardly. The problem is that DOS does not
have any such concept as STDERR, at least not in
anything like the Unix sense of that term.
Traditionally, DOS software
does not print error messages to the console unless
they are fatal and the program is bailing back out
to the prompt immediately, in which case there was
(think DOS here, not a command box window on Windows,
which is intended as a compatibility measure only)
no point whatsoever in being able to redirect that
output,
since it would typically be one sentence long and
the user would be getting a prompt. (Remember that
DOS was not intended to be Unix, to run on university
computers where people were developing software and
doing research; it was intended to sell to businesses
for things like spreadsheets.) For more verbose
error messages in the DOS world, you either explain
it onscreen in a fashion that is integrated with the
rest of your app (think: text-based dialog box) or
else you write it to a log file. For these hysterical
raisins, DOS did not have a redirectable STDERR, which
leads to your current problem. All C libraries for
DOS and for Windows face this issue: do we want to
support compiling Unix software that assumes it can
just write willy nilly to STDERR, and if so how? The
usual approach is to make STDERR synonymous with the
console, which is IMO absolutely the wrong way to do
it. (What the C libraries probably should have done,
back in the eighties, is point it to C:\STDERR.LOG,
but it's far too late for that now. What C libraries
IMO should be fixed to do now is show these messages
in a window available by clicking an icon in the
system tray -- but nobody listens to me.) What just
about all C libraries do, as I said, is map it to
the non-redirectable console. (The console is not
by its nature intended to be redirectable; it is not
in any sense a stream. The BIOS calls that most C
libraries use for writing to it can just as well be
used to draw text-based pulldown menus and dialog
boxes; they support absolute positioning and that
sort of jazz, rather more like ncurses than STDERR
on Unix.)
As a result, the C library's standard error stream is
largely useless on DOS -- and also on Win32 by
extension. My advice is, don't use it, in Perl or
any other language, if your code needs to support
Windows.
However, Windows 95 and later come with some facilities
like multitasking that will allow you to fake it,
particularly since in Perl it is possible to close
off STDERR and open it pointing to something else
(something _other_ than the C library's concept of
STDERR), such as a pipe or log file. This is what
you should do.
$;=sub{$/};@;=map{my($a,$b)=($_,$;);$;=sub{$a.$b->()}}
split//,".rekcah lreP rehtona tsuJ";$\=$ ;->();print$/
| [reply] [Watch: Dir/Any] [d/l] |
|
|