# Improve Process API

**URL:** <https://forum.crystal-lang.org/t/improve-process-api/579>\
**Category:** Crystal Contrib\
**Created:** [March 21, 2019, 4:33pm UTC](https://forum.crystal-lang.org/t/improve-process-api/579 "2019-03-21T16:33:38Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![j8r](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/j8r/32/111_2.png) [@j8r](https://forum.crystal-lang.org/u/j8r)\
**Post date:** [March 21, 2019, 4:33pm UTC](https://forum.crystal-lang.org/t/improve-process-api/579/1 "2019-03-21T16:33:38Z")

</div>

I find the `Process` API with `Process.new` and `Process.run` confusing, often mixing both forgetting which one is blocking or non-blocking.  
Another pain point compared to said, Python, is to easily create Strings from the STDOUT and STDERR from a subprocess.  
I created [https://github.com/j8r/exec.cr](https://github.com/j8r/exec.cr) to experiment how to improve this API.

`Exec.run` is always non blocking (we can have a variant taking no blocks for the stdlib)

```auto
Exec.new("/bin/ls", ["/tmp", "-l"]) {}

```

To change it to blocking

```auto
Exec.new "/bin/true", &.wait

```

And finally to catch the STDOUT and STDERR to strings, and puts its success

```auto
output, error = Exec.run "echo hello", { |process| puts process.wait.success? } #=> true
puts output.to_s #=> hello

```

Discard the basic shell argument interpreter in the library for this proposal.

This is essentially a draft, the main points I want to underline is:

- can we have a `output, error = Process.new` API?
- can we remove the blocking `Process.run` and only have the non-blocking `Process.new` to remove any confusion/duplication?

---

<div class="post-metadata">

**Author:** ![stronny](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/stronny/32/228_2.png) [@stronny](https://forum.crystal-lang.org/u/stronny)\
**Post date:** [March 21, 2019, 6:38pm UTC](https://forum.crystal-lang.org/t/improve-process-api/579/2 "2019-03-21T18:38:01Z")

</div>

I would say that process management in Crystal is not very usable in general at the moment. The most common use-case would probably be this: block the current fiber until the child exits, then give me its exit status, stdout and stderr. A slight tweak would be to additionally feed its stdin.

The other use-case, and a more general problem is an async event-driven flow: start this child, and while it runs feed its stdin, and read its out and err in chunks, and process these chunks. The problem here is a mental model to be able to not go insane. Fibers and channels do not help here at all, what I think would work is libev’s event-loop, something like this:

```auto
loop do
  event = sleep_until_one_of_these_events_happen \
    child.exited,
    child.stdin.ready_to_write,
    child.stdout.ready_to_read,
    child.stderr.ready_to_read,
    timeout.fired,
    its_time_to_stop.raised # something like a sigint to this fiber
  process event # with overloading depending on event's class
end

```

It’s useful to aggregate different events in a single fiber to avoid a thin paste of one-line fibers all over the code, which is of course just my opinion.

---

<div class="post-metadata">

**Author:** ![paulcsmith](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/paulcsmith/32/301_2.png) [@paulcsmith](https://forum.crystal-lang.org/u/paulcsmith)\
**Post date:** [March 21, 2019, 6:52pm UTC](https://forum.crystal-lang.org/t/improve-process-api/579/3 "2019-03-21T18:52:27Z")

</div>

I also have trouble with the Process syntax because the defaults require a lot of arguments and it is not always clear whether is is blocking or not.

In all my projects I write some kind of helper method that wraps `Process` because of that.

### Clarify blocking v. non-blocking

I’m not sure why Process by default would be non-blocking, but either way I think we could clarify this by calling the methods what they are so it is always Crystal clear what is going on

```crystal
Process.sync(...args)
Process.async(...args)

```

I think that makes it incredibly easy to tell what it is doing. You don’t need to pull up the docs to know what these 2 are doing.

Not totally sold on the names, but the principles stand I think. The API could be `Process.async` and `Process.wait` or something. But in general if there are two ways to do something giving them clear names is a good way to go IMO.

## Capturing output

I _love_ the idea of making working without output easier. I think rather than returning output and error, it could return a `ProcessResult` object that had an `#output` and `#error`. This way you don’t have to remember which position `output` is. Instead you have a nice object with documentation and that can be enhanced with additional methods in the future if helpful:

```crystal
result = Process.sync "echo hello"
puts result.output.to_s #=> hello

```

It could also have `#status` that returns a `Process::Status` for example. I think returning a `ProcessResult` would be super helpful and flexible

## Shell true

Another part that is confusing to me is when to use `shell: true`. It is not clear from the documentation when to use it or not. I don’t think this is a problem with the API, but maybe some more .examples/clarification around this would be helpful.

I’d be happy to contribute but I honestly don’t know the difference between `shell: true/false`

## Error Handling

Right now by default a failed process does not raise an error. This has generated a number of bugs for me when I forget to handle errors. It takes awhile to figure out where the problem is when there is no error.

My suggestion here is to make them raise an error by default and have `?` versions that don’t raise. Or if we don’t want a breaking change (or just don’t like the `?` version), introduce a `!` version so that it is at least super easy raise when there is an issue, e.g. `Process.sync`

---

<div class="post-metadata">

**Author:** ![j8r](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/j8r/32/111_2.png) [@j8r](https://forum.crystal-lang.org/u/j8r)\
**Post date:** [March 22, 2019, 12:42pm UTC](https://forum.crystal-lang.org/t/improve-process-api/579/4 "2019-03-22T12:42:41Z")

</div>

`shell: true` invokes a `/bin/sh -c 'mycommand'` subprocesss that executes the command (so it may also create a sub-subprocess). `shell: false` directly execute the command, you need to have the arguments “pre-parsed” as an `Enumerable`.

---

<div class="post-metadata">

**Author:** ![stronny](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/stronny/32/228_2.png) [@stronny](https://forum.crystal-lang.org/u/stronny)\
**Post date:** [March 22, 2019, 1:06pm UTC](https://forum.crystal-lang.org/t/improve-process-api/579/5 "2019-03-22T13:06:35Z")

</div>

And unless you use `exec` in the shell command you will get neither the pid, nor the exit status of your command run by shell.

---

<div class="post-metadata">

**Author:** ![j8r](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/j8r/32/111_2.png) [@j8r](https://forum.crystal-lang.org/u/j8r)\
**Post date:** [April 22, 2019, 4:03pm UTC](https://forum.crystal-lang.org/t/improve-process-api/579/6 "2019-04-22T16:03:16Z")

</div>

At least I am not alone to find room for improvement in `Process` 🙂.  
I’ll open an RFC.

---

<div class="post-metadata">

**Author:** ![absolutejam\_gh](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/absolutejam_gh/32/501_2.png) [@absolutejam\_gh](https://forum.crystal-lang.org/u/absolutejam_gh)\
**Post date:** [July 26, 2019, 7:49pm UTC](https://forum.crystal-lang.org/t/improve-process-api/579/7 "2019-07-26T19:49:06Z")

</div>

Didn’t realise this thread existed because it was one of my points in [Stdlib - A mixed discussion about some modules](https://forum.crystal-lang.org/t/stdlib-a-mixed-discussion-about-some-modules/986/2). If you have an RFC, I’d be interested in seeing it!

---

<div class="post-metadata">

**Author:** ![straight-shoota](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/straight-shoota/32/36_2.png) [@straight-shoota](https://forum.crystal-lang.org/u/straight-shoota)\
**Post date:** [March 10, 2026, 5:21pm UTC](https://forum.crystal-lang.org/t/improve-process-api/579/8 "2026-03-10T17:21:34Z")

</div>

There is some movement on the Process API: [RFC 0025: Modern Process Spawn API](https://forum.crystal-lang.org/t/rfc-0025-modern-process-spawn-api/8773) proposes changes that cover most of the suggestions by @paulcsmith here. 🎉
