Command-Run

 view release on metacpan or  search on metacpan

README.md  view on Meta::CPAN

callee's perspective.  `@ARGV`, `$0`, and `$_` are protected with
`local` to prevent side effects.

## How Raw Mode Works

The `raw` option controls which PerlIO layer is applied to the
temporary files used for I/O redirection:

    # Normal mode (raw => 0):
    binmode $tmpfile, ':encoding(utf8)';  # full encode/decode

    # Raw mode (raw => 1):
    binmode $tmpfile, ':utf8';            # flag only, no conversion

In the normal fork path, `:encoding(utf8)` is necessary because data
crosses process boundaries through pipes as byte streams.  But in
nofork mode, caller and callee share the same Perl interpreter, so
Perl's internal string format (which is already UTF-8 internally) can
be passed directly.  The `:utf8` layer simply sets Perl's UTF-8 flag
on strings read from the file without performing actual byte-level
conversion.

### PerlIO Encoding Layer Accumulation

Nofork mode temporarily redirects the standard filehandles and
restores them with `open FH, '>&', ...`.  Perl keeps the
existing PerlIO layer stack when a filehandle is re-opened this way,
and `binmode FH, ':encoding(utf8)'` pushes a new layer even when one
is already present.  In earlier versions of this module, the encoding
layer pushed on each execution therefore accumulated on STDIN/STDOUT
one layer per execution, making long-running processes progressively
slower (nofork could end up slower than fork) and growing memory
without bound.  See
[https://github.com/kaz-utashiro/perl-perlio-leak-bench](https://github.com/kaz-utashiro/perl-perlio-leak-bench) for the
underlying Perl behavior.

This is fixed: the layer change is now undone before the handles are
restored, so the layer stack of the standard filehandles stays
exactly as the caller left it.  With the fix, nofork is much faster
than fork in either mode, and most of the historical gap between
`:encoding` and raw mode (which was caused by the accumulation) is
gone:

    # Benchmark: code ref with stdin (100-byte input,
    # 1000 iterations, object reused)
    fork:                    495/s (baseline)
    nofork + :encoding:   15,997/s (32x)
    nofork + :utf8 (raw): 20,038/s (40x)

## Zero-Modification Callee Integration

A key advantage of this mechanism is that **callee modules typically
require no modification** to work with nofork+raw mode.

Many Perl modules use `use open` pragma or equivalent to set up
encoding layers on standard I/O:

    package App::ansicolumn;
    use open IO => ':utf8', ':std';    # sets :encoding(utf8) on STDIO

This works transparently because of execution order.  When using
nofork mode with method chaining:

    require App::ansicolumn;           # (1) module loaded here
    Command::Run->new
        ->command(\&ansicolumn, @args)
        ->with(stdin => $text, nofork => 1, raw => 1)
        ->update                       # (2) STDOUT redirected here
        ->data;

At step (1), `require` loads the module and `use open ':std'`
applies `:encoding(utf8)` to the **original** STDOUT.  At step (2),
nofork mode redirects STDOUT to a fresh temporary file with
`:utf8` layer.  The callee's encoding setup has already fired on the
original STDOUT and does not affect the redirected one.

This means existing modules like [App::ansicolumn](https://metacpan.org/pod/App%3A%3Aansicolumn) and
[App::ansifold](https://metacpan.org/pod/App%3A%3Aansifold) work unchanged with nofork+raw mode, achieving
significant speedups with zero code changes on the callee side.

## Caller Protection

Nofork mode executes the code reference in the same process, so care
is needed to prevent the callee from corrupting the caller's state.
The following protections are applied:

- `local $_;`

    Prevents the callee from modifying the caller's `$_`.  This is
    critical when the caller aliases `$_` to important data (e.g.,
    greple's `local *_ = shift` to alias `$_` to the content buffer).
    Without this protection, a callee's `while (<>)` loop
    would set `$_` to `undef` at EOF, destroying the caller's data.

- `local @ARGV`

    Prevents the callee from modifying the caller's `@ARGV`.

- `$0` save/restore

    Prevents the callee from permanently changing the program name.

# COMPARISON WITH SIMILAR MODULES

There are many modules on CPAN for executing external commands.
This module is designed to be simple and lightweight, with minimal
dependencies.

This module was originally developed as [App::cdif::Command](https://metacpan.org/pod/App%3A%3Acdif%3A%3ACommand) and
has been used in production as part of the [App::cdif](https://metacpan.org/pod/App%3A%3Acdif) distribution
since 2014.  It has also been adopted by several unrelated modules,
which motivated its release as an independent distribution.

- [IPC::Run](https://metacpan.org/pod/IPC%3A%3ARun)

    Full-featured module for running processes with support for
    pipelines, pseudo-ttys, and timeouts.  Very powerful but large
    (135KB+) with non-core dependencies ([IO::Pty](https://metacpan.org/pod/IO%3A%3APty)).  Overkill for
    simple command execution.

- [Capture::Tiny](https://metacpan.org/pod/Capture%3A%3ATiny)



( run in 1.283 second using v1.01-cache-2.11-cpan-7fcb06a456a )