xhudik has asked for the wisdom of the Perl Monks concerning the following question:

Hi Monks, I'm trying to write a simple perl script that would execute program (e.g. cat), and then make some interaction with the program. I'm using IPC::Run since it is able to emulate terminal (pty) which is needed for the interaction (some putting and getting data from the subprocess). My program:
#!/usr/bin/perl -w use IPC::Run qw(start pump finish timeout); my $tok_program = "/bin/./cat"; my ($TOK_IN, $TOK_OUT, $TOK_ERR); my $TOK = start [$tok_program], '<pty<', \$TOK_IN,'>pty>',\$TOK_OUT, ' +2>', \$TOK_ERR , debug => 3 or die "Error: $?;\n"; while (my $line = <STDIN> ) { chomp($line); $TOK_IN = $line; $TOK_OUT = ''; $TOK_ERR = ''; print "Input2pump:$TOK_IN\n"; pump $TOK while length $TOK_IN; print "out:$TOK_OUT\n"; }
Basicaly, it should execute cat program and then post each input to the "cat" and wait for the answer. However, the output is not what it was supposed to be :). (The output can differs from time ti time - sometimes is completely empty, sometimes just 1 entry is empty,...) Obviously, I'm missing something but no idea what Any help will be appreciate!
testing file a:
1. line
2. line
The command:
./wrap < a
And the output is:
IPC::Run 0000 0123---7---- [#1(20574)]: debugging fd is 3 IPC::Run 0000 0123---7---- [#1(20574)]: ** starting IPC::Run 0000 0123---7---- [#1(20574)]: opening pty '0' IPC::Run 0000 012345-7---- [#1(20574)]: pty() = ( 4, 5 ) IPC::Run 0000 012345-7---- [#1(20574)]: '/bin/./cat' is absolute IPC::Run 0000 012345-7---- [#1(20574)]: kid to read 0 from pty '0' IPC::Run 0000 012345-7---- [#1(20574)]: kid 1 to read 0 from SCALAR vi +a pty '0' IPC::Run 0000 012345-7---- [#1(20574)]: kid 1 to write 1 to SCALAR via + pty '0' IPC::Run 0000 012345-7---- [#1(20574)]: kid 1 to write 2 to SCALAR IPC::Run 0000 012345678--- [#1(20574)]: pipe() = ( 6, 8 ) IPC::Run 0000 012345678--- [#1(20574)]: kid 1[]'s 0 is my 4 IPC::Run 0000 012345678--- [#1(20574)]: kid 1[]'s 1 is my 4 IPC::Run 0000 012345678--- [#1(20574)]: kid 1[]'s 2 is my 6 IPC::Run 0000 012345678--- [#1(20574)]: child: `'/bin/./cat'` IPC::Run 0000 012345678--- [#1(20574)]: opening sync pipe IPC::Run 0000 01234567890- [#1(20574)]: pipe() = ( 9, 10 ) IPC::Run 0000 01234567890- [#1(20574)]: fork() = 20575 IPC::Run 0000 0123456789-- [#1(20574)]: close( 10 ) = 0 IPC::Run 0000 01234567890- [#1(20575) cat]: Cleaning up parent's ptty +'0' IPC::Run 0000 0123-567890- [#1(20575) cat]: closing stdin, out, err IPC::Run 0000 ---3-567890- [#1(20575) cat]: open fds: 6 8 4 9 10 5 IPC::Run 0000 ---3-5-7890- [#1(20575) cat]: close( 6 ) = 0 IPC::Run 0000 ---3-5-78-0- [#1(20575) cat]: close( 9 ) = 0 IPC::Run 0000 0--3-5-78-0- [#1(20575) cat]: dup2( 5, 0 ) = 0 IPC::Run 0000 01-3-5-78-0- [#1(20575) cat]: dup2( 5, 1 ) = 1 IPC::Run 0000 0123-5-78-0- [#1(20575) cat]: dup2( 1, 2 ) = 2 IPC::Run 0000 0123-5-78-0- [#1(20575) cat]: dup2( 8, 2 ) = 2 IPC::Run 0000 0123---78-0- [#1(20575) cat]: close( 5 ) = 0 IPC::Run 0000 0123---7--0- [#1(20575) cat]: close( 8 ) = 0 IPC::Run 0000 0123---7--0- [#1(20575) cat]: execing /bin/./cat IPC::Run 0000 0123---7--0- [#1(20575) cat]: exec()ing '/bin/./cat' IPC::Run 0000 0123456789-- [#1(20574)]: read( 9 ) = 0 but true chars ' +' IPC::Run 0000 012345678--- [#1(20574)]: close( 9 ) = 0 IPC::Run 0000 01234-67---- [#1(20574)]: close( 8 ) = 0 Input2pump:1. line IPC::Run 0000 01234-67---- [#1(20574)]: ** pumping IPC::Run 0000 01234-67---- [#1(20574)]: fds for select: ----b-r----- IPC::Run 0000 01234-67---- [#1(20574)]: timeout=forever IPC::Run 0000 01234-67---- [#1(20574)]: selected ----w------- IPC::Run 0000 01234-67---- [#1(20574)]: filtering data to fd 4 (kid's +stdin) IPC::Run 0000 01234-67---- [#1(20574)]: writing to fd 4 (kid's stdin) IPC::Run 0000 01234-67---- [#1(20574)]: write( 4, '1. line' ) = 7 IPC::Run 0000 01234-67---- [#1(20574)]: exiting _select(): io occured +and break_on_io set out: Input2pump:2. line IPC::Run 0000 01234-67---- [#1(20574)]: ** pumping IPC::Run 0000 01234-67---- [#1(20574)]: fds for select: ----b-r----- IPC::Run 0000 01234-67---- [#1(20574)]: timeout=forever IPC::Run 0000 01234-67---- [#1(20574)]: selected ----b------- IPC::Run 0000 01234-67---- [#1(20574)]: filtering data to fd 4 (kid's +stdin) IPC::Run 0000 01234-67---- [#1(20574)]: writing to fd 4 (kid's stdin) IPC::Run 0000 01234-67---- [#1(20574)]: write( 4, '2. line' ) = 7 IPC::Run 0000 01234-67---- [#1(20574)]: filtering data from fd 4 (kid' +s stdout) IPC::Run 0000 01234-67---- [#1(20574)]: reading from fd 4 (kid's stdou +t) IPC::Run 0000 01234-67---- [#1(20574)]: read( 4 ) = 7 chars '1. line' IPC::Run 0000 01234-67---- [#1(20574)]: exiting _select(): io occured +and break_on_io set out:1. line
Here, the output is written but the expected output is:
1. line
2. line
or, the output without debug info:
Input2pump:1. line out: Input2pump:2. line out:
Here, there is nothing in the output
Thank you, Tomas

Replies are listed 'Best First'.
Re: IPC::Run and subprocess interaction
by ikegami (Patriarch) on Jul 14, 2011 at 19:08 UTC

    Two problems.

    • cat still buffers its output when its STDOUT is connected to a terminal. Like Perl's default, it uses block buffering when its STDOUT isn't connected to a terminal and line buffering when it is. You'll have to send a newline to get it to react.

    • You're program is too fast! cat never has a change to run. You need to wait for output from cat.

    #!/usr/bin/perl -w use strict; use IPC::Run qw( start pump finish timeout ); my @tok_program = 'cat'; my ($TOK_IN, $TOK_OUT, $TOK_ERR); my $TOK = start \@tok_program, '<', \$TOK_IN, '1>pty>', \$TOK_OUT, '2>', \$TOK_ERR or die "Error: $?;\n"; while (my $line = <STDIN> ) { # Send input. $TOK_IN = $line; pump $TOK while length $TOK_IN; # Wait for output. pump $TOK while $TOK_OUT !~ /\n\z/; print "out: $TOK_OUT"; $TOK_OUT = ''; } finish $TOK or die "returned $?";
    • No reason to make STDIN a pty.
    • Added missing call to finish.
    • >pty> changed STDOUT and STDERR. Switched to 1>pty>.

    Expect also uses a pty, and it might be better suited to control interactive application.

      Thanks ikegami. You are right. Hmm, so my basic mistake was not to put the second pump (for reading back to $TOK_OUT). During my Internet browsing I didn't find any example with 2 pumps.

      Just for the sake of curiosity. $TOK_OUT doesn't seem to be an ordinary variable. If you run the following program:

      #!/usr/bin/perl -w use IPC::Run qw(start pump finish timeout); my $tok_program = "/bin/./cat"; my ($TOK_IN, $TOK_OUT, $TOK_ERR); my $TOK = start [$tok_program], '<', \$TOK_IN, '1>pty>',\$TOK_OUT, '2>', \$TOK_ERR , debug => 0 or die "Error: $?;\n"; while (my $line = <STDIN> ) { # Send input. $TOK_IN = $line; pump $TOK while length $TOK_IN; # Wait for output. pump $TOK while $TOK_OUT !~ /\n\z/; my $msg = $TOK_OUT; chomp($msg); print "out:$TOK_OUT;;MESSAGE=$msg;;END\n"; $TOK_OUT = ''; } finish($TOK) or die "returned: $?";
      You will get:
      out:1. line ;;ENDSAGE=1. line out:2. line ;;ENDSAGE=2. line
      Instead of:
      out:1. line ;;MESSAGE=1. line;;END out:2. line ;;MESAGE=2. line;;END
      Do you know why?

      Once more - thank you!

        During my Internet browsing I didn't find any example with 2 pumps.

        Only one is needed. This would do:

        $TOK_IN = $line; pump $TOK while $TOK_OUT !~ /\n\z/; print "out: $TOK_OUT"; $TOK_OUT = '';

        This one is in the docs.

        $TOK_OUT doesn't seem to be an ordinary variable.

        Change chomp to s/\r?\n\z//.

Re: IPC::Run and subprocess interaction
by runrig (Abbot) on Jul 14, 2011 at 18:23 UTC
    My guess: 'cat' is buffering the input because there are no linefeeds. If you don't chomp the lines, you get the expected output at the expected time.

    Update: Nevermind. I am likely wrong.