views:

75

answers:

5

I have a terrible nested script structure I am maintaining that takes user input from the keyboard periodically and I am trying to write a script that will automate this input. Essentially, the script structure looks like this:

  • cmd1.csh takes 3 lines of input and then calls cmd2.csh, then exits normally
  • cmd2.csh calls cmd3.pl twice and then takes 1 line of input, then exits normally
  • cmd3.pl takes 1 line of input and exits normally.

Unfortunately, I can't permanently change any of these scripts, so anything I can do has to be done as a wrapper around the whole script chain.

I've tried several variations of the following perl script, but maybe I'm just not hitting the magic combination of quotes and escapes:

$cmd = "echo 'a\\
b\\
c\\
d\\
e\\
f' | ~/pltest/cmd1.csh";

system_tcsh

sub system_tcsh
{
   @args = ("tcsh", "-c", shift);
   return system(@args);
}

The problem seems to be that once cmd3.pl is called once and d is read, no more input is read by either the second call to cmd3.pl or by cmd2.csh. Putting multiple reads in cmd3.pl works fine and more items are read off of STDIN, but once that first call to cmd3.pl completes, STDIN is empty (or in some kind of unuseable state).

What happens to STDIN when the perl script returns and is there any way to simulate this kind of input using perl or something else?

Thanks in advance.

A: 

And what happens if you do:

echo 'a\
b\
c\
d\
e\
f' > tmpfile
cat tmpfile | ~/pltest/cmd1.csh

If that does work you could organise your Perl script around pre-fetching input, saving it to a temp file and then doing the above cat /path/to/tempfile | /path/to/cmd1.csh trick.

+1  A: 

To start with, I would use Expect to script input, it's probably more reliable, and available on most Unix systems.

Barring that, depending on your version of echo, many allow use of "\n" in quoted strings, although that should be functionally equivalent. Something like:

$cmd = '/bin/echo -e "a\nb\nc\nd\ne\nf" | ~/pltest/cmd1.csh'

(This works on my Linux box.. man echo to see what your local version is capable of.)

But, without actually looking at how the Perl script is reading input, it's hard to guess exactly where the output is going, and this is certainly a very convoluted situation.

just jon
+1  A: 

I created the conditions to reproduce the problem, but all went well:

caller.pl:

#! /usr/bin/perl

$cmd = "echo 'a\\
b\\
c\\
d\\
e\\
f' | ~/doc/Answers/src/pltest/cmd1.csh";

sub system_tcsh
{
   @args = ("tcsh", "-c", $cmd);
   return system(@args);
}

system_tcsh

cmd1.csh:

#! /bin/csh
echo "${0}(cmd1) $argv[*]"
set line1 = $<
set line2 = $<
set line3 = $<
setenv lineno  3
echo "cmd1: read: $line1 $line2 $line3"
./cmd2.csh

cmd2.csh:

#! /bin/csh
echo "    ${0}(cmd2) $argv[*] now running..."
./cmd3.csh
./cmd3.csh

set line6 = $<

echo "    ${0}: Read: $line6"

cmd3.csh:

#! /bin/csh
# cmd3
set line = $<
echo "        ${0}: Read: '$line'"

Trial run:

frayser@gentoo ~/doc/Answers/src/pltest $ ./caller.pl 
/export/home/frayser/doc/Answers/src/pltest/cmd1.csh(cmd1) 
cmd1: read: a b c
    ./cmd2.csh(cmd2)  now running...
        ./cmd3.csh: Read: 'd'
        ./cmd3.csh: Read: 'e'
    ./cmd2.csh: Read: f

Maybe you can modify this to recreate the trouble, or use it to implement your solution.

--

UPDATE
This is an update that reproduces the Perl STDIN problem, and provides a work-around for it.

Replacing cmd3 with a Perl version gives the reported, problematic result:

cmd3.pl that replaces cmd3.csh:

#! /usr/bin/perl
# cmd3

$_=<STDIN>;
chomp();
print "        $0: Read: '$_'\n";

Result: using cmd3.pl. After the "d" is read, no more input is available.

./caller.pl    
./cmd1.csh(cmd1) 
cmd1: read: a b c
    ./cmd2.csh(cmd2)  now running...
        ./cmd3.pl: Read: 'd'
        ./cmd3.pl: Read: ''
    ./cmd2.csh: Read: 

In order to remedy this situation, cmd2 is changed to only send 1 line to cmd3. The line (1) command handily does this:

#! /bin/csh
echo "    ${0}(cmd2) $argv[*] now running..."
line | ./cmd3.pl
line | ./cmd3.pl

set line6 = $<

echo "    ${0}: Read: $line6"

This is cmd2.csh optimized to avoid overhead of executing the line command.

#! /bin/csh
echo "    ${0}(cmd2) $argv[*] now running..."
set x = $<
echo $x | ./cmd3.pl

set y = $<
echo $y | ./cmd3.pl

set line6 = $<

echo "    ${0}: Read: $line6"

Here is the output with the updated cmd2.csh. The functionality is now the same using Perl as it was with using csh as the final script: no lost of stdin.

./cmd1.csh(cmd1) 
cmd1: read: a b c
    ./cmd2.csh(cmd2)  now running...
        ./cmd3.pl: Read: 'd'
        ./cmd3.pl: Read: 'e'
    ./cmd2.csh: Read: f
Frayser
[Tested the binmode fix](http://stackoverflow.com/questions/3809357/how-do-i-recover-stdin-from-a-perl-script-that-was-called-from-a-csh-script/3865528#3865528)Setting `binmode STDIN, "unix"` in *cmd3.pl*(and no other changes) has been verified as a simpler solution.
Frayser
A: 

@just jon: Unfortunately, expect is not available to me, and I was trying to find a way with the tools I have (because our request process isn't exactly expedient). Thanks for the response, though! The perl script is just performing a , or <>, neither of which seemed to work properly.

@user268396: That doesn't seem to work either, and I would imagine it is the functional equivalent since the pipe forces the input into STDIN regardless of where the data is coming from.

@Frayser: Have you tried the third script (cmd3.csh) as a perl script? That is what my problem involves.

Stephen
@Stephen Just noticed your answer. Wow! With *cmd3* in Perl, after reading once, none of the other commands could get any more input, not even the next Perl invocation on *cmd3*. If I find a solution, I'll post.
Frayser
@Stephen cmd2.csh has to be adjusted so that it only provides one line to Perl (*cmd3.pl*): `set aline=$<; echo "$aline" | ./cmd3.pl` Hopefully the script can be modified to read 1 then echo only 1 line to Perl; that fixed the problem in the test case.
Frayser
A: 

This answer about greedy perl stdin from Greg Bacon seems to be a method to solve this. Of course, it means I have to change the innermost perl script, but it's the best solution so far if I can get permission to change it. It isn't the most robust solution - probably using the Expect pm is the best, but barring that, this should work.

Stephen