Command-Run

 view release on metacpan or  search on metacpan

README.md  view on Meta::CPAN

    `:encoding(utf8)` layers that encode and decode UTF-8 on every read
    and write.

The `nofork` option eliminates the fork cost by executing the code
reference in the current process.  The `raw` option eliminates the
encoding cost by using the `:utf8` PerlIO pseudo-layer instead of
`:encoding(utf8)`.

Combined, these options can achieve **over 30x speedup** compared to
fork-based execution for lightweight functions with small I/O.

## How Nofork Works

In nofork mode, `_execute_nofork` temporarily redirects the real
STDOUT, STDERR, and STDIN file descriptors to temporary files using
`dup`, executes the code reference, then restores them:

    # Simplified flow:
    open $save, '>&', \*STDOUT;           # save original
    open STDOUT, '>&', $tmpfile;          # redirect to tmpfile
    $code->(@args);                       # execute code ref
    open STDOUT, '>&', $save;             # restore original
    $tmpfile->seek(0, 0);
    $output = do { local $/; <$tmpfile> }; # read captured output

The code reference sees real STDOUT/STDIN file descriptors (not tied
handles), so it behaves identically to the fork path from the
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 Leak

There is an additional reason to prefer `:utf8` over
`:encoding(utf8)` in long-running processes.  Repeatedly pushing and
popping the `:encoding(utf8)` layer (which happens on each nofork
execution when opening and closing temporary files) causes a
cumulative performance degradation in Perl's PerlIO subsystem.  This
affects **all** PerlIO operations in the process, not just the ones
using the encoding layer.

In benchmarks, nofork with `:encoding(utf8)` is actually **slower**
than fork after many iterations, due to this leak.  Raw mode avoids
the issue entirely.

    # Benchmark: code ref with stdin (100-byte input, 1000 iterations)
    fork:                  399/s (baseline)
    nofork + :encoding:    316/s (0.8x — slower than fork!)
    nofork + :utf8 (raw): 13,433/s (34x faster)

## 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),
`_execute_nofork` 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.



( run in 0.509 second using v1.01-cache-2.11-cpan-71847e10f99 )