```{index} single: pipe open, single: open; to process, pair: pipe; child process, single: $? ``` # Pipes The same [`open`](../../p5/core/perlfunc/open) that attaches a handle to a file can attach it to a child process. Two modes select the direction: - `"-|"` — you *read* the child's standard output. - `"|-"` — you *write* to the child's standard input. In both cases the child runs concurrently with your Perl program, and its exit status becomes available in `$?` when the handle is closed. ```{index} single: pipe open; read from command, single: -|, single: fork, single: exec, single: glob ``` ## Reading from a command ```perl open my $fh, "-|", "sort", "-u", glob("unsorted/*.txt") or die "pipe: $!"; while (my $line = <$fh>) { # ... process one sorted unique line at a time ... } close $fh or die pipe_close_diag($?, $!); ``` What happens: - Perl `fork`s. In the child, `stdout` is connected to the write end of a pipe, and the command is `exec`ed. - In the parent, `$fh` reads from the other end. As far as your code is concerned, it is a normal read handle — [`readline`](../../p5/core/perlfunc/readline), `read`, `<$fh>`, and so on all work. - When you finish reading, `close` waits for the child to exit and records its status in `$?`. ```{index} single: pipe open; write to command, single: |-, single: close; pipe ``` ## Writing to a command ```perl open my $fh, "|-", "gzip", "-9", "-c" or die "pipe: $!"; for my $line (@records) { print $fh $line, "\n"; } close $fh or die pipe_close_diag($?, $!); ``` Symmetry: the child's `stdin` is connected to the read end of the pipe; your parent writes to the other end; `close` waits for the child to exit. One behaviour that often surprises people: `close` on a write-pipe flushes everything buffered, shuts the pipe's write end, and *only then* waits for the child. If the child is slow, `close` blocks until the child is done. That is almost always what you want. ```{index} single: pipe open; list form, single: pipe open; string form, single: shell injection, single: /bin/sh ``` ## List form versus string form Both `open` calls above used the **list form**: ```perl open my $fh, "-|", "sort", "-u", glob("unsorted/*.txt") or die; ``` Perl recognises that the third and subsequent arguments form a command and its argv. It does **not** involve a shell: - No word splitting, no glob expansion, no redirection, no variable substitution. - A filename containing a space or a `;` is just a filename. - There is no shell to mis-quote against, so the class of bugs known as "shell injection" is structurally impossible. The alternative is the **string form**: ```perl open my $fh, "-|", "sort -u unsorted/*.txt" or die; ``` Perl passes the single string to `/bin/sh -c`. The shell does word splitting, glob expansion, redirection, everything. That is what makes this form useful — you can write `"cat -n > numbered.txt"` on the command side and the shell sets up the `>` for you — and what makes it dangerous when any piece of the string came from outside your program. Rule of thumb: **list form by default, string form only when a shell feature is the point**. When a filename glob is the reason to use a shell, consider pre-expanding the glob with Perl's own `glob` built-in and staying in list form, as the example at the top of this page did. ```{index} single: $?; decoding, single: wait(2), single: signal; child process, single: exit status ``` ## Decoding `$?` After a pipe is closed, `$?` is set the same way `wait(2)` returns: a 16-bit value with the low byte carrying signal information and the high byte carrying the exit code. In Perl terms: - `$? == 0` — the child exited cleanly with status 0. Success. - `$? == -1` — `close` could not even wait for the child, because something went wrong before `fork`/`exec`. `$!` has detail. - `$? & 0x7F` — signal number if the child died on a signal. `$? & 0x80` (the high bit of the low byte) is set if the child dumped core. - `$? >> 8` — exit status if the child exited normally. A diagnostic helper pulls these apart: ```perl sub pipe_close_diag { my ($status, $err) = @_; return "pipe close: $err" if $status == -1; my $signal = $status & 0x7F; return "child died on signal $signal" if $signal; my $exit = $status >> 8; return "child exited with status $exit" if $exit; return "child exited cleanly"; } ``` Pair that with every pipe `close`: ```perl close $fh or die pipe_close_diag($?, $!); ``` Not checking the close on a pipe is the same mistake as not checking it on a write handle — except louder, because a child process failed silently in the background. ```{index} single: bidirectional pipe, single: IPC::Open2, single: IPC::Open3, single: co-process ``` ## One command, one pipe `open` with `-|` or `|-` gives you a pipe in *one* direction. There is no standard way to get both directions from a single `open` call, because two-way pipes to a co-process require careful buffering and deadlock avoidance that the `open` shape cannot express. When you need that, reach for `IPC::Open2` or `IPC::Open3` — built for the job, with the handshake caveats documented in their own POD. ## Next The third kind of thing `open` can attach a handle to is neither a file nor a process — it is a string in memory. See [In-memory handles](in-memory).