i am calling a perl script client.pl from a main script to capture the output of client.pl
in #output.
is there anyway to avoid the use of these two files so i can use the output of client.pl in main.pl itself
here is my code....
main.pl
=======
my #output = readpipe("client.pl");
client.pl
=========
#! /usr/bin/perl -w
#use strict;
use Socket;
#initialize host and port
my $host = shift || $FTP_SERVER;
my $port = shift || $CLIENT_PORT;
my $proto = getprotobyname('tcp');
#get the port address
my $iaddr = inet_aton($host);
my $paddr = sockaddr_in($port, $iaddr);
#create the socket, connect to the port
socket(SOCKET, PF_INET, SOCK_STREAM, $proto)or die "socket: $!\n";
connect(SOCKET, $paddr) or die "connect: $!\n";
my $line;
while ($line = <SOCKET>)
{
print "$line\n";
}
close SOCKET or die "close: $!";
/rocky..
Put the common code in a package. Use the package in client.pl and main.pl. Chapter 10 of Programming Perl has more information.
Not sure what you are really trying to do, but might worh investigating a package such as Net::FTP ( http://search.cpan.org/perldoc?Net%3A%3AFTP )
you can do two things:
Merge the codes in client.pl and main.pl as your main function does no work other than printing. In case you want to do more from the incoming input data, you should do that in client.pl itself, coz an in-memory array(#output) may run out of RAM while reading large size data across the network.
If you want the output in an array (#output)
sub client {
# intialize ..
my #array = (); #empty array
while ($line = <SOCKET>)
{
push(#array,$line);
}
return #array;
}
#output = client();
print #output;
Other way, you can also use references:
sub client {
# intialize ..
my #array = (); #empty array
while ($line = <SOCKET>)
{
push(#array,$line);
}
return #array;
}
my $output_ref = client();
print #$output_ref; // dereference and print.
Related
Using Perl's Net::Telnet module to retrieve data from upsd.
There is one particular function I'm trying to implement, retrieving the data for a single var.
The problem is only a single line is output, and that line is used to match
Prompt, so it is not output.
Here's raw telnet:
telnet dns1 3493
Trying 192.168.15.1...
Connected to dns1.
Escape character is '^]'.
get var cp1500 ups.test.result
VAR cp1500 ups.test.result "Done and passed"
Connection closed by foreign host.
Here's some code:
#!/usr/bin/perl
use strict;
use warnings;
use Net::Telnet;
my $host = "dns1";
my $model = "cp1500";
my $bvar = "ups.test.result";
my $t = new Net::Telnet (Timeout => 3, Port => 3493, Prompt => "/VAR $model $bvar/");
$t->open($host);
my #ary = $t->cmd("get var $model $bvar");
print #ary,"\n";
This just prints the newline as the array is empty. Prompt is matched else there'd be a timeout error. How can I get that single line of output back for processing in the script?
This is my solution, use Socket instead of Net::Telnet.
#!/usr/bin/perl
use strict;
use warnings;
use Socket;
my $host = 'str003';
my $port = 3493;
my $model = 'cp1350';
my $quer = 'get var';
my $bvar = 'ups.test.result';
my ($sock,$iaddr,$paddr,$send);
$iaddr = inet_aton($host);
$paddr = sockaddr_in($port, $iaddr);
$send = join(' ',$quer,$model,$bvar);
socket($sock, AF_INET, SOCK_STREAM, 6) or die $!;
connect($sock , $paddr) or die "connect failed : $!";
send($sock , "$send\nlogout\n" , 0);
while (my $line = <$sock>)
{
if ($line =~ /^VAR/) {
print "$line\n";
}
}
close($sock);
This is the one where one line of data is returned:
VAR cp1350 ups.test.result "Done and passed"
I'm following this guide explaining how to do a server using IO::Async but I'm having issues with my client code. I have it where I send first then receive. This makes me press enter on each client before receiving any data. I figured I'd have to listen till I wanted to type something but I'm not really sure how. Below is my current client code.
use IO::Socket::INET;
# auto-flush on socket
$| = 1;
# create a connecting socket
my $socket = new IO::Socket::INET (
PeerHost => 'localhost',
PeerPort => '12345',
Proto => 'tcp',
);
die "cannot connect to the server $!\n" unless $socket;
print "My chat room client. Version One.\n";
while (1) {
my $data = <STDIN>;
$socket->send($data);
my $response = "";
$socket->recv($response, 1024);
print ">$response";
last if (index($data, "logout") == 0);
}
$socket->close();
I actually had this problem myself a few weeks ago when trying to make a client/server chat for fun.
Put it off until now.
The answer to your problem of having to hit enter to receive data, is that you need to use threads. But even if you use threads, if you do $socket->recv(my $data, 1024) you won't be able to write anything on the command line.
This isn't using your code, but here is my solution after banging my head against a wall for the last 24hrs. I wanted to add this as an answer, because though the question is out there on stackoverflow, none of the answers seemed to show how to use IO::Select.
Here is the server.pl script, it does not use threading:
#!/usr/bin/perl
use strict;
use warnings;
use IO::Socket::INET;
use IO::Select;
$| = 1;
my $serv = IO::Socket::INET->new(
LocalAddr => '0.0.0.0',
LocalPort => '5000',
Reuse => 1,
Listen => 1,
);
$serv or die "$!";
print 'server up...';
my $sel = IO::Select->new($serv); #initializing IO::Select with an IO::Handle / Socket
print "\nAwaiting Connections\n";
#can_read ( [ TIMEOUT ] )
#can_write ( [ TIMEOUT ] )
#add ( HANDLES )
#http://perldoc.perl.org/IO/Select.html
while(1){
if(my #ready = $sel->can_read(0)){ #polls the IO::Select object for IO::Handles / Sockets that can be read from
while(my $sock = shift(#ready)){
if($sock == $serv){
my $client = $sock->accept();
my $paddr = $client->peeraddr();
my $pport = $client->peerport();
print "New connection from $paddr on $pport";
$sel->add($client); #Adds new IO::Handle /Socket to IO::Select, so that it can be polled
#for read/writability with can_read and can_write
}
else{
$sock->recv(my $data, 1024) or die "$!";
if($data){
for my $clients ($sel->can_write(0)){
if($clients == $serv){next}
print $clients $data;
}
}
}
}
}
}
And the client.pl, which uses threads:
#!/usr/bin/perl
use strict;
use warnings;
use IO::Socket::INET;
use threads;
use IO::Select;
$| = 1;
my $sock = IO::Socket::INET->new("localhost:5000");
$sock or die "$!";
my $sel = IO::Select->new($sock);
print "Connected to Socket ". $sock->peeraddr().":" . $sock->peerport() . "\n";
#This creates a thread that will be used to take info from STDIN and send it out
#through the socket.
threads->create(
sub {
while(1){
my $line = <>;
chomp($line);
for my $out (my #ready = $sel->can_write(0)){
print $out $line;
}
}
}
);
while(1){
if(my #ready = $sel->can_read(0)){
for my $sock(#ready){
$sock->recv(my $data, 1024) or die $!;
print "$data\n" if $data;
}
}
}
There is one other problem that arises though, when the client receives data and prints it to the console, your cursor goes to a new line, leaving behind any characters you had typed.
Hope this helps and answers your question.
For a simple "just send from STDIN, receive to STDOUT" client, you could use any of telnet, nc or socat. These will be simple enough to use for testing.
$ telnet localhost 12345
$ nc localhost 12345
$ socat stdio tcp:localhost:12345
If you actually want to write something in Perl, because you want to use it as an initial base to start a better client from, you probably want to base that on IO::Async. You could then use the netcat-like example here. That will give you a client that looks-and-feels a lot like a simple netcat.
I am guessing you need to set the MSG_DONTWAIT flag on your recv call, and print the response only if it is non-null.
$socket->recv($response, 1024, MSG_DONTWAIT);
print ">$response" if ($response ne "");
I'm using the following script to capture STDIN, STDOUT and STDERR from the command passed as an argument.
#!/usr/bin/perl
use strict;
use warnings;
use IPC::Open3;
local(*CMD_IN, *CMD_OUT, *CMD_ERR);
my $pid = open3(*CMD_IN, *CMD_OUT, *CMD_ERR, $ARGV[0]);
close(CMD_IN);
my #stdout_output = <CMD_OUT>;
my #stderr_output = <CMD_ERR>;
close(CMD_OUT);
close(CMD_ERR);
waitpid ($pid, 0); # reap the exit code
print "OUT:\n", #stdout_output;
print "ERR:\n", #stderr_output;
It all works good with the exception that I'm not sure how to monitor if the command passed is hung. Could you please suggest a way?
I've borrowed this snippet originally from 'Programming Perl'.
You can use select or IO::Select and provide a timeout. If you want to read both from stdout and stderr, you should do that anyway (see the documentation of IPC::Open3).
Here's an example program using IO::Select:
#!/usr/bin/perl
use strict;
use warnings;
use IO::Select;
use IPC::Open3;
use Symbol 'gensym';
my ($cmd_in, $cmd_out, $cmd_err);
$cmd_err = gensym;
my $pid = open3($cmd_in, $cmd_out, $cmd_err, $ARGV[0]);
close($cmd_in);
my $select = IO::Select->new($cmd_out, $cmd_err);
my $stdout_output = '';
my $stderr_output = '';
while (my #ready = $select->can_read(5)) {
foreach my $handle (#ready) {
if (sysread($handle, my $buf, 4096)) {
if ($handle == $cmd_out) {
$stdout_output .= $buf;
}
else {
$stderr_output .= $buf;
}
}
else {
# EOF or error
$select->remove($handle);
}
}
}
if ($select->count) {
print "Timed out\n";
kill('TERM', $pid);
}
close($cmd_out);
close($cmd_err);
waitpid($pid, 0); # reap the exit code
print "OUT:\n", $stdout_output;
print "ERR:\n", $stderr_output;
Notes:
I use lexical vars for file handles. This requires the use of gensym for the stderr handle.
The argument to can_read is the timeout in seconds.
I use sysread for non-buffered IO.
I terminate the child if there's a read timeout.
I came up with the following solution heavily based on this answer.
However using select and avoiding signals as in nwellnhof's example looks much cleaner which is why I accepted it. I'm posting it here if somebody is interested:
my $pid = open3(*CMD_IN, *CMD_OUT, *CMD_ERR, $cmd);
if ($pid > 0){
eval{
local $SIG{ALRM} = sub {kill 9, $pid;};
alarm 6;
waitpid($pid, 0);
alarm 0;
};
}
I'm writing a Perl script that takes in a list of URLs and checks to see if they exist. (Note that I only care if they exist; I don’t care what their contents are. Here’s the important part of the program.
use LWP::Simple qw($ua head);
if (head($url))
{
$numberAlive ++;
}
else
{
$numberDead ++;
}
Right now the program works fine; however, I want it to run faster. Thus I'm considering making it multithreaded. I assume that the slow part of my program is contacting the server for each URL; therefore, I'm looking for a way in which I can send out requests to the URLs of other webpages on my list while I'm waiting for the first response. How can I do this? As far as I can tell, the head routine doesn't have a callback that can get called once the server has responded.
Begin with familiar-looking front matter.
#! /usr/bin/env perl
use strict;
use warnings;
use 5.10.0; # for // (defined-or)
use IO::Handle;
use IO::Select;
use LWP::Simple;
use POSIX qw/ :sys_wait_h /;
use Socket;
Global constants control program execution.
my $DEBUG = 0;
my $EXIT_COMMAND = "<EXIT>";
my $NJOBS = 10;
URLs to check arrive one per line on a worker’s end of the socket. For each URL, the worker calls LWP::Simple::head to determine whether the resource is fetchable. The worker then writes back to the socket a line of the form url : *status* where *status* is either "YES" or "NO" and represents the space character.
If the URL is $EXIT_COMMAND, then the worker exits immediately.
sub check_sites {
my($s) = #_;
warn "$0: [$$]: waiting for URL" if $DEBUG;
while (<$s>) {
chomp;
warn "$0: [$$]: got '$_'" if $DEBUG;
exit 0 if $_ eq $EXIT_COMMAND;
print $s "$_: ", (head($_) ? "YES" : "NO"), "\n";
}
die "NOTREACHED";
}
To create a worker, we start by creating a socketpair. The parent process will use one end and each worker (child) will use the other. We disable buffering at both ends and add the parent end to our IO::Select instance. We also note each child’s process ID so we can wait for all workers to finish.
sub create_worker {
my($sel,$kidpid) = #_;
socketpair my $parent, my $kid, AF_UNIX, SOCK_STREAM, PF_UNSPEC
or die "$0: socketpair: $!";
$_->autoflush(1) for $parent, $kid;
my $pid = fork // die "$0: fork: $!";
if ($pid) {
++$kidpid->{$pid};
close $kid or die "$0: close: $!";
$sel->add($parent);
}
else {
close $parent or die "$0: close: $!";
check_sites $kid;
die "NOTREACHED";
}
}
To dispatch URLs, the parent grabs as many readers as are available and hands out the same number of URLs from the job queue. Any workers that remain after the job queue is empty receive the exit command.
Note that print will fail if the underlying worker has already exited. The parent must ignore SIGPIPE to prevent immediate termination.
sub dispatch_jobs {
my($sel,$jobs) = #_;
foreach my $s ($sel->can_write) {
my $url = #$jobs ? shift #$jobs : $EXIT_COMMAND;
warn "$0 [$$]: sending '$url' to fd ", fileno $s if $DEBUG;
print $s $url, "\n" or $sel->remove($s);
}
}
By the time control reaches read_results, the workers have been created and received work. Now the parent uses can_read to wait for results to arrive from one or more workers. A defined result is an answer from the current worker, and an undefined result means the child has exited and closed the other end of the socket.
sub read_results {
my($sel,$results) = #_;
warn "$0 [$$]: waiting for readers" if $DEBUG;
foreach my $s ($sel->can_read) {
warn "$0: [$$]: reading from fd ", fileno $s if $DEBUG;
if (defined(my $result = <$s>)) {
chomp $result;
push #$results, $result;
warn "$0 [$$]: got '$result' from fd ", fileno $s if $DEBUG;
}
else {
warn "$0 [$$]: eof from fd ", fileno $s if $DEBUG;
$sel->remove($s);
}
}
}
The parent must keep track of live workers in order to collect all results.
sub reap_workers {
my($kidpid) = #_;
while ((my $pid = waitpid -1, WNOHANG) > 0) {
warn "$0: [$$]: reaped $pid" if $DEBUG;
delete $kidpid->{$pid};
}
}
Running the pool executes the subs above to dispatch all URLs and return all results.
sub run_pool {
my($n,#jobs) = #_;
my $sel = IO::Select->new;
my %kidpid;
my #results;
create_worker $sel, \%kidpid for 1 .. $n;
local $SIG{PIPE} = "IGNORE"; # writes to dead workers will fail
while (#jobs || keys %kidpid || $sel->handles) {
dispatch_jobs $sel, \#jobs;
read_results $sel, \#results;
reap_workers \%kidpid;
}
warn "$0 [$$]: returning #results" if $DEBUG;
#results;
}
Using an example main program
my #jobs = qw(
bogus
http://stackoverflow.com/
http://www.google.com/
http://www.yahoo.com/
);
my #results = run_pool $NJOBS, #jobs;
print $_, "\n" for #results;
the output is
bogus: NO
http://www.google.com/: YES
http://stackoverflow.com/: YES
http://www.yahoo.com/: YES
Another option is HTTP::Async.
#!/usr/bin/perl
use strict;
use warnings;
use HTTP::Request;
use HTTP::Async;
my $numberAlive = 0;
my $numberDead = 0;
my #urls = ('http://www.perl.com','http://www.example.xyzzy/foo.html');
my $async = HTTP::Async->new;
# you might want to wrap this in a loop to deal with #urls in batches
foreach my $url (#urls){
$async->add( HTTP::Request->new( HEAD => $url ) );
}
while ( my $response = $async->wait_for_next_response ) {
if ($response->code == 200){$numberAlive ++;}
else{$numberDead ++;}
}
print "$numberAlive Alive, $numberDead Dead\n";
Worker-based parallelisation (using your choice of threads or processes):
use strict;
use warnings;
use feature qw( say );
use threads; # or: use forks;
use LWP::Simple qw( head );
use Thread::Queue::Any qw( );
use constant NUM_WORKERS => 10; # Or whatever.
my $req_q = Thread::Queue::Any->new();
my $resp_q = Thread::Queue::Any->new();
my #workers;
for (1..NUM_WORKERS) {
push #workers, async {
while (my $url = $req_q->dequeue()) {
my $is_alive = head($url) ? 1 : 0;
$resp_q->enqueue($is_alive);
}
};
}
$req_q->enqueue($_) for #urls;
my ($alive, $dead);
for (1..#urls) {
my $is_alive = $resp_q->dequeue();
++( $is_alive ? $alive : $dead );
}
$req_q->enqueue(undef) for #workers;
$_->join for #workers;
say $alive;
say $dead;
My OS is Archlinux with perl 5.14.2. I am just trying to write a little program to accomplish a remote comlile. The program just passes a C source file to the server. The server will call gcc to compile the C code and pass the compiler's message. The client can't receive the compiler's message. I have the message in the server.
There is the code:
#!/usr/bin/perl -w
# oj.pl --- alpha
use warnings;
use strict;
use IO::File;
use IO::Socket;
use constant MY_TRAN_PORT => 138000;
$| = 1;
my $tmpFileToBeCompiled = IO::File->new ("> tmpFile09090989.c") or die "Can't creat this file";
#if (defined $tmpFileToBeCompiled) {
# print $tmpFileToBeCompiled "argh"; # just for test!
#}
# $fihi->close;
my $port = shift || MY_TRAN_PORT;
my $sock_server = IO::Socket::INET->new (Listen => 20,
LocalPort => $port,
Timeout => 60,
Reuse => 1)
or die "Can't create listening socket: $!\n";
my $tmp = 1;
while ($tmp) {
next unless my $session = $sock_server->accept;
my $peer = gethostbyaddr ($session->peeraddr, AF_INET)
|| $session->peerhost;
warn "Connection from [$peer, $port]\n";
while (<$session>) {
print $tmpFileToBeCompiled $_; # if it works, the filehandle should be changed into tmpFile. just fixed.
print $session "test!";
}
my #lines = `gcc tmpFile09090989.c 2>&1`;
foreach ( #lines) {
print $session $_ . "test!!!\n";
# $session->print;
}
print "OK!";
$tmpFileToBeCompiled->close;
warn "Connecting finished!\n";
$session->close;
$tmp --;
}
$sock_server->close;
----------------------------------------end--------------------------------------------------------
-------------------------------------client.pl--------------------------------------------------------
use warnings;
use strict;
use IO::Socket qw(:DEFAULT);
use File::Copy;
use constant MY_TRAN_PORT => 138000;
use IO::File;
my $host = shift || '127.0.0.1';
my $port = shift || MY_TRAN_PORT;
my $socket = IO::Socket::INET->new("$host:$port") or die $#;
my $fh = IO::File->new("a.c", "r");
my $child = fork();
die "Can't fork: $!\n" unless defined $child;
# if (!$child) {
# $SIG{CHLD} = sub { exit 0 };
# userToHost();
# print "Run userToHost done!\n";
# $socket->shutdown(1);
# sleep;
# } else {
# hostToUser();
# print "Run hostToUser done! \n";
# warn "Connection closed by foreign host\n";
# }
userToHost();
unless ($child) {
hostToUser();
print "Run hostToUser done! \n";
warn "Connection closed by foreign host\n";
$socket->close;
}
sub userToHost {
while (<$fh>) {
# print $_; # for debug
print $socket $_;
}
}
sub hostToUser {
while (<$socket >) {
print $_;
}
}
# copy ("a.c", $socket) or die "Copy failed: $!";
print "Done!";
You don't need to fork in client. At all. Just like themel said
You have error in client code: <$socket > should be <$socket>
You need to notify server that you have written all data and server can start compilation. Otherwise server will stuck at while (<$session>) forever.
To achieve this you could call shutdown($socket, 1) which means you finished writing. See perldoc -f shutdown
Final prototype (very rough) could look like this: https://gist.github.com/19b589b8fc8072e3cfff
yko nailed it, but let me just suggest that your task will be solved in a much easier and more maintainable way by a shell script running from inetd.