Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Chriss Miller on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Manipulating another program in Perl

Status
Not open for further replies.

shiufai

Programmer
Joined
Nov 21, 2006
Messages
6
Location
JP
I am given a program in which when it is called, the user can type on the shell and some formatted text would be ouput by the program to the shell.

My task is to build an 'intermediate' script that can call the forementioned program, allow the user to type on the shell, read the program's output, modify and output the new formatted texts.

The purpose of this is to change the output interface of the previous program to cope with a newer version of the entire system.

It does not sound like a very complicated task but I am quite new to Perl. And I am stuck with the (calling, allowing the user to input and reading the program's output) part.

Any help would be greatly appreciated!!
 
Can you use a pipe or other form of redirection to get the input into the program, rather than going interactive and letting the user type it in directly?

Steve

[small]"Every program can be reduced by one instruction, and every program has at least one bug. Therefore, any program can be reduced to one instruction which doesn't work." (Object::PerlDesignPatterns)[/small]
 
For now, I am just trying to mimic what the previous program was capable to do so to be sure the new intermediate script can be used by the original system.

And therefore I mentioned the user input part.

but ultimately, the script is used internally in which the system will pass strings into the script instead of letting the user to do it directly.

THanks for reply!!
 
Ultimately, fine. But you still need to know if it can read STDIN, as this will have a big impact on your design.

Steve

[small]"Every program can be reduced by one instruction, and every program has at least one bug. Therefore, any program can be reduced to one instruction which doesn't work." (Object::PerlDesignPatterns)[/small]
 
I think I understand what you mean now.

Am I dealing with a threading problem here? I want the new version of the script to read from STDIN (or another source of input) and at the same time have the previous version of the program open (which would also read from STDIN or the same source of input).

So I think i am struggling with trying to achieve the
1. new script read from input source
2. new script pass string into old script
3. new script read output from old script
4. new script return reformatted output of old script

so it would have problems if i have the two scripts running simultaneously?

I mean, ultimately, i can create a while loop in the new script that calls and terminates the old script each time the new script receives something from the input source.

Hmm it does not seem to be a very efficient/well-designed solution. Is there a better way to achieve this so that I only have to call the old script once inside the new script and would work for the entire duration of the program?
 
No, you won't have a problem with the two running simultaneously. Script A blocks while it is invoking script B; assuming that you have set up the data properly, script B should return its output to script A without stopping to wait for user input.
Hmm it does not seem to be a very efficient/well-designed solution. Is there a better way to achieve this so that I only have to call the old script once inside the new script and would work for the entire duration of the program?
Well, that depends. Considering at the moment your script waits around for keyboard input, then the relative efficiency shouldn't be too much of an issue for you.

Typically you don't want to get into a two-way producer-consumer relationship between the two scripts unless you really have no alternative.



Steve

[small]"Every program can be reduced by one instruction, and every program has at least one bug. Therefore, any program can be reduced to one instruction which doesn't work." (Object::PerlDesignPatterns)[/small]
 
I don't understand this requirement:

1. new script read from input source


do you mean invoke/start new script?


- Kevin, perl coder unexceptional!
 
What you need here is Perl Cookbook Recipe 16.8: Controlling Input and Output of Another Program

from book said:
Problem: You want to both write to and read from another program. The open function lets you do one or the other, but not both.

Solution:

Use the standard IPC::Open2 module:

Code:
use IPC::Open2;

open2(*README, *WRITEME, $program);
print WRITEME "here's your input\n";
$output = <README>;
close(WRITEME);
close(README);
Lots more discussion in the actual book

Docs:
 
still stuck... ARG! I will try to define my problem better with some non-working script that I wrote:

I have two programs (old version and new version) that pretty much do the same thing but output in different formats. When called from shell, the user would be prompted for input. Let's say he enteres 'i am a boy' into the shell...

old version script would output, for example:
i/1 am/2 a/3 boy/4

new version script would output, for example:
i/1//i am/2//am a/3//a boy/4//boy

I need to write another script that can be called from shell, prompts user for input, passes input to the new version script but output in the old version format.

I tried using the IPC::Open2 module:

Code:
open2(*READ, *WRITE, "new version script location");
while(<>){  #keep prompting for user input
    print WRITE $_;
    my $output = <READ>;
    #reformat $output
    print $output;
}

But that did not work...hmmm

thanks!!
 
barebones:

Code:
print "Enter something: ";
chomp(my $input = <STDIN>);
system("oldscript $input");#will not retutn output back to program
my $output = `newscript $input`;#will return out back to program
print $output;

- Kevin, perl coder unexceptional!
 
hmmm great! I think the backtcik trick temporarily solved my problem. Though maybe still not the best to call the bew version script everytime inside a while loop...

Using it for now to implement the rest of the system first.

Thanks for all the help!
 
untested code:

Code:
use IPC::Open2;
my $pid1 = open2(*READ, *WRITE, "old_program");
my $pid2 = open (NEW, "| new_program") or die "$!";
print "Enter 'Q' to terminate program\n";
print "Enter some data: ";
while(<>){
    chomp($_);   
    if ($_ eq 'Q') {
       kill 1, $pid1, $pid2;
       exit;
    }
    print NEW $_; 
    print WRITE $_;
    my $output = <READ> || "no output\n";
    print $output;
}

- Kevin, perl coder unexceptional!
 
I took a moment to experiement with IPC::Open2. Most of this is based off the documentation available on cpan for IPC::Open2 and the following reference:


The following script simply takes in lines of input and prints out the text in reverse. The user types quit to exit.

reverse.pl
Code:
$| = 1; # No Buffering

while (<>) {
	chomp;
	last if /^quit$/;
	print "$0: " . scalar reverse . "\n";
}

1;

__END__

The following is a wrapper for the reverse script. It also just accepts data from the user, but then it passes it to the reverse script and prints out the results in a reformatted way.

wrap.pl
Code:
use IPC::Open2;
use IO::Handle;

use strict;

# Start Child Process
my $reader = IO::Handle->new;
my $writer = IO::Handle->new;
my $cmd = 'perl reverse.pl';
my $pid = open2($reader, $writer, $cmd);
$reader->autoflush(1);
$writer->autoflush(1);

# Cleanup
END {
	$writer->print("quit\n");
	$writer->close();
	$reader->close();
	kill $pid, 9;
}

print "Enter 'quit' to terminate program\n";
print "Enter some data:\n";

while (<>) {
	chomp;
	last if /^quit$/;
	$writer->print("$_\n");
	my $line = $reader->getline();
	print "$0: $line";
}

1;

__END__

Now, the above code is very simple. There are obvious enhancements that can be made by adding signal handlers for CHLD, INT, and __DIE__, but in truth the END block should handle those for now, be it not very gracefully. The one very important piece of code I couldn't get to work is the reading of multiple lines of input from the $reader without already knowing how much data that you are expecting. getlines() appears to block, as does getline() if you are at the end of the current pipe. And eof() doesn't return any useful information.

Anyway, running this example should at least give you a feeling for what is possible, and therefore give you ideas on how you might approach your problem.

Results:
Code:
>perl wrap.pl
Enter 'quit' to terminate program
Enter some data:
foo bar baz
wrap.pl: reverse.pl: zab rab oof
more fake data to reverse
wrap.pl: reverse.pl: esrever ot atad ekaf erom
quit

>

Good luck
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top