Elixir provides several options for interacting with external process in particular it offers the [[Handling and Executing External Processes#Port|Port]] library and the [[Handling and Executing External Processes#System.cmd|System.cmd]] wrapper
## Notes
- `:EXIT` codes from system commands can bubble up causing undesired side effects
- a good workaround pattern is to use a simple Genserver to handle the task invocation and management.
## Known Issues
Handling process exit can be tricky:
- [handle_info/2 FunctionClauseError](https://elixirforum.com/t/using-erlports-and-jaxon-to-grab-youtube-metadata-via-python-handle-info-2-functionclauseerror/31628)
- [Solution: supervising async tasks](https://elixirforum.com/t/supervising-async-tasks/14412/3)
## References
### [System.cmd](https://hexdocs.pm/elixir/System.html#cmd/3)
This function returns a tuple containing the collected result and the command exit status.
Internally, this function uses a Port for interacting with the outside world. However, if you plan to run a long-running program, ports guarantee stdin/stdout devices will be closed but it does not automatically terminate the program. The documentation for the Port module describes this problem and possible solutions under the "Zombie processes" section.
```elixir
Examples
System.cmd("echo", ["hello"])
{"hello\n", 0}
System.cmd("echo", ["hello"], env: [{"MIX_ENV", "test"}])
{"hello\n", 0}
System.cmd("echo", ["hello"], into: IO.stream(:stdio, :line))
hello
{%IO.Stream{}, 0}
```
### [Port](https://hexdocs.pm/elixir/Port.html)
> Ports provide a mechanism to start operating system processes external to the Erlang VM and communicate with them via message passing.
```elixir
port = Port.open({:spawn, "cat"}, [:binary])
send(port, {self(), {:command, "hello"}})
send(port, {self(), {:command, "world"}})
flush()
{#Port<0.1444>, {:data, "hello"}}
{#Port<0.1444>, {:data, "world"}}
send(port, {self(), :close})
:ok
flush()
{#Port<0.1464>, :closed}
:ok
```
#### Message and function APIs
There are two APIs for working with ports. It can be either asynchronous via message passing, as in the example above, or by calling the functions on this module.
The messages supported by ports and their counterpart function APIs are listed below:
- `{pid, {:command, binary}}` - sends the given data to the port. See command/3.
- `{pid, :close}` - closes the port. Unless the port is already closed, the port will reply with {port, :closed} message once it has flushed its buffers and effectively closed. See close/1.
- `{pid, {:connect, new_pid}}` - sets the new_pid as the new owner of the port. Once a port is opened, the port is linked and connected to the caller process and communication to the port only happens through the connected process. This message makes new_pid the new connected processes. Unless the port is dead, the port will reply to the old owner with {port, :connected}. See connect/2.
On its turn, the port will send the connected process the following messages:
- `{port, {:data, data}`} - data sent by the port
- `{port, :closed}` - reply to the {pid, :close} message
- `{port, :connected}` - reply to the {pid, {:connect, new_pid}} message
- `{:EXIT, port, reason}` - exit signals in case the port crashes. If reason is not :normal, this message will only be received if the owner process is trapping exits