running "less" from perl pipeline
dup, exec, fork, perl, pipe
Solution
It's an interesting timing issue:
- Shell forks, creates a process group, sets the terminal foreground group to it, and execs `perl`.
- `perl` forks and execs `less`.
- `perl` execs `echo`.
- `echo` exits, shell believes job is done and sets itself as the terminal foreground process group once again.
- `less` tries to acquire terminal. It is not in the terminal foreground process group. `less` fails.
The simplest solution: change `fork` to `!fork` (or equivalently, swap your `if` and `else` blocks). That way, the final stage of the pipeline defines the lifetime of the job. (If you watch carefully, the shell forks and execs processes in this same order when it runs a pipeline, too.)
Problem
I am trying to set up arbitrary pipelines from Perl, in much the same way as a shell might. This has the desired effect, it's like "echo foo | sed s/oo/ar/": ``` #!/usr/bin/perl use strict; use IO::Handle; $| = 1; my ($first_prog, $second_prog) = ([qw(echo foo)], [qw(sed s/oo/ar/)]); #$second_prog = ["less"]; pipe(my $second_prog_input, my $first_prog_output) or die "problem setting up pipe: $!\n"; if (fork) { STDOUT->fdopen(fileno($first_prog_output), 'w') or die; exec(@$first_prog) or die; } else { STDIN->fdopen(fileno($second_prog_input), 'r') or die; exec(@$second_prog) or die "couldn't exec: $!: command was @$first_prog\n"; } ``` However, when I make the second argument "less", my terminal flashes, and I don't see the output in the pager. Other than the brief flash, there is no sign of less running at all. Now something I don't get at all is that the following does behave like "echo foo | less": ``` pipe(my $first_prog_output, STDIN) or die "problem setting up pipe: $!\n"; my ($first_prog, $second_prog) = ([qw(echo foo)], ["less"]); open($first_prog_output, "-|", @$first_prog) or die "$!: command was @$first_prog\n"; exec(@$second_prog) or die "couldn't exec: $!: command was @$first_prog\n"; ``` But I don't understand at all what that call to pipe() is doing. The first argument is supposed to be a "reader", and the second one a "writer". How is STDIN a "writer"? I'm very confused by all this, and think there may be something fundamental about the underlying Unix API that I'm missing or I've forgotten.