{"atom":"regression/proc-run-shell","family":"regression","slug":"proc-run-shell","header":{"atom":"regression/proc-run-shell","kind":"whole-program — a minimal repro, not yet reduced to an atom","source":"curated"},"axes":null,"cols":null,"engines":["rakudo-2026.07","rakupp-3.14.0"],"kinds":["ref","pp"],"defFrom":"rakupp:t/regression/proc-run-shell.raku","counts":{"a":1,"f":0,"d":0,"c":0,"r":0,"n":0},"files":["atoms/regression/proc-run-shell.grid"],"records":[["0001","","# Regression (github.com/ash/rakupp issue #10): `run`/`shell` on Windows.\n#\n#   * `shell` ran its argument through a hardcoded `/bin/sh -c`. There is no\n#     /bin/sh on Windows, so EVERY shell() call there failed with exitcode -1 and\n#     no output whatsoever. It uses %COMSPEC% (cmd.exe) there now.\n#   * the Windows spawn passed GetStdHandle(STD_INPUT_HANDLE) straight into\n#     STARTF_USESTDHANDLES, which requires all three handles to be valid AND\n#     inheritable. GetStdHandle guarantees neither — with stdin redirected, or no\n#     console at all, it can hand back INVALID_HANDLE_VALUE and then CreateProcess\n#     fails for every command.\n#   * a failed spawn returned exitcode -1 in silence. That is undiagnosable: the\n#     reporter saw `exitcode -1`, empty out-str, empty err-str and nothing else.\n#     The OS error text is reported now.\n#   * CreateProcess cannot start a .bat/.cmd directly, so a not-found spawn\n#     retries through the command processor.\n#   * every argument was quoted unconditionally when building the Windows command\n#     line, which breaks a switch — cmd.exe does not recognise a quoted `\"/c\"`.\n#     Quoting is now applied only to arguments that need it.\n#\n# The Windows halves cannot be exercised here; what this pins is that the POSIX\n# behaviour they share is unchanged, including the argument-quoting rule.\n# Contract: exit 0 + last line PASS.\nmy @fail;\nsub check($got, $want, $what) { @fail.push(\"$what: got $got want $want\") unless $got eq $want }\n\n# shell goes through the command processor, so its argument is one command line\ncheck(shell('echo hello', :out).out.slurp(:close).trim, 'hello', 'shell captures stdout');\ncheck(shell('echo a b   c', :out).out.slurp(:close).trim, 'a b c', 'the shell collapses the spacing');\ncheck(shell('exit 3').exitcode, '3', 'and the exit code comes back');\n\n# run takes an argv — an argument containing a space stays ONE argument\ncheck(run('echo', 'x y', :out).out.slurp(:close).trim, 'x y', 'a spaced argument survives');\ncheck(run('printf', '%s-%s', 'a', 'b', :out).out.slurp(:close), 'a-b', 'several arguments');\ncheck(run('printf', '%s', '', :out).out.slurp(:close), '', 'an EMPTY argument is still an argument');\ncheck(run('printf', '%s', 'a\"b', :out).out.slurp(:close), 'a\"b', 'and one containing a quote');\n\n# .command reports what was asked for\ncheck(run('echo', 'hi', :out).command.join('|'), 'echo|hi', 'run .command is the argv');\ncheck(shell('echo hi', :out).command.join('|'), 'echo hi', 'shell .command is the command string');\n\n# a missing command does not throw; it reports through the Proc\nmy $missing = run('definitely-not-a-real-cmd-xyz', :out, :err);\ncheck(($missing.exitcode != 0).gist, 'True', 'a missing command is a nonzero exit');\n\n# A Proc gists as a Proc, not as a dump of its slots. The generic hash rendering\n# printed every key including out-str, so `say shell(\"ls\")` showed the listing\n# TWICE — once echoed as the child ran, once inside the gist.\n#\n# Asserted on a NON-captured Proc: with `:out` still open Rakudo prints a much\n# richer self-referential form (`my \\Proc_… = Proc.new(out => IO::Pipe.new(proc\n# => Proc_…, …))`) that we do not reproduce, and that is not what was reported.\nmy $pg = run('true').gist;\ncheck($pg.starts-with('Proc.new(in => IO::Pipe, out => IO::Pipe, err => IO::Pipe, os-error => Str, exitcode => 0'),\n      True, \"a Proc gists in Rakudo's shape\");\ncheck($pg.contains('command => (\"true\",)'), True, 'a ONE-element argv keeps its trailing comma');\ncheck($pg.contains('out-str'), False, 'and NOT its captured output');\ncheck(($pg ~~ /'pid => ' \\d+/).so, True, 'and reports a real pid');\nmy $pm = run('echo', 'hi').gist;\ncheck($pm.contains('command => (\"echo\", \"hi\")'), True, 'a two-element argv, quoted');\n\nif @fail { note \"FAILED: @fail.join('; ')\"; say 'FAIL' } else { say 'PASS' }","output hi\nPASS","a",["hi …(2 lines)","hi …(2 lines)"],0,[["budget","20s"]]]]}