zapateo

zapateo

Launch a system command with stdin/stdout access

Hi everyone, this is my first post on this forum :slight_smile:
I am learning Elixir and I wish to write my software/scripts with it, I really love the syntax.
One problem I encountered is launching a system command and interact with it through stdin/stdout.
For example something like:

System.cmd("sudo", ["whoami"])
System.cmd("vi", [])

doesn’t work at all because they require access to stdin/stdout (seems like they can’t find a tty).
I searched a lot on the web and I found some resources with some workaround (eg. using a GUI askpass for sudo or launching system commands in another terminal); such resources I have found are a bit old.

My question is: is the situation changed? Is there any way, for example using Ports, to make the commands above work “as expected” like in other programming language?

For example in Python I write:

import subprocess
subprocess.run("vi")

This is the only “missing feature” that prevents me from porting all my software and scripts to Elixir.

Thanks in advance :slight_smile:

Marked As Solved

jayjun

jayjun

I do like Elixir as a scripting language except the BEAM is notoriously bad at working with external commands, even worse when those require the terminal.

For starters, this works.

port = Port.open({:spawn, "vi"}, [:nouse_stdio, :exit_status])

receive do
  {^port, {:exit_status, exit_status}} -> IO.puts "vi exited with #{exit_status}"
end

But ports are really designed to work with external programs custom tailored for your Erlang app.

One assumption is Erlang must talk to your external process, so pipes are connected to the child’s standard input/output. :nouse_stdio means “use file descriptors 3 and 4 instead” so vi inherits the terminal as standard input/output and things work as expected. System.cmd—which is simply a convenience wrapper around ports—won’t work because it is hardcoded to :use_stdio.

Next, Port.open does not block like subprocess.run. To mimic that, you can ask for the :exit_status then block with receive.

Finally, child processes spawned by Erlang ports are detached from the controlling terminal. So programs that read from the terminal directly like sudo won’t work. There’s no general workaround, only application-specific ones.

As you can see, it’s an uphill battle but keep in mind that Python is first and foremost a scripting language. If you don’t need to call external programs, .exs files and escripts are a pleasant way to automate. Otherwise, libraries can help ease some hurdles but not entirely.

Also Liked

NobbZ

NobbZ

I know that sudo doesn’t even use stdin/stdout but reads from the (virtual) tty directly. I assume similar for vi.

To be honest, I wouldn’t even expect your python code to work…

And why port everything to Elixir? If you already have scripts in python that do their job, then keep them that way… I still prefer using zsh and python for doing scripts that I run from the terminal or via cron.

I use Elixir mainly for long running and daemon style applications.

outlog

outlog

+1 on porcelain, though check out GitHub - jayjun/rambo: Run your command. Send input. Get output. · GitHub as well, as I believe that supersedes porcelain.. (I have only ever used porcelain few years back to do some pdf scraping..)

benwilson512

benwilson512

Author of Craft GraphQL APIs in Elixir with Absinthe

Hi @zapateo, you may want to check out Porcelain – porcelain v2.0.3, it has a spawn function that should be relatively easy to use for something like this.

I do agree with @NobbZ overall with respect to this idea though. Use the right tool for the right job. If you already have functioning python scripts for this stuff and there isn’t internal concurrency issues that a port to Elixir would provide clear wins with, I’d consider whether a port like this provides value. If you want to do it for educational reasons then that’s perfectly fine, but if you’re trying to solve problems, consider whether the problem is a good fit for what you have or a better fit with Elixir.

Last Post!

zapateo

zapateo

Thanks a lot. Your answer clarified to me many aspects of the problem.

Also, the snippet you posted works as expected; so simple but very powerful, that it what I was looking for.

:slight_smile:

Where Next?

Popular in Questions Top

rms.mrcs
Hi, I need to transform a list of numbers into a map where the keys are the indexes and the values are the original values of the list. ...
New
vertexbuffer
Hello, can anybody help here..? I have a list of players and I what to delete an element, but every for loop the list is reverting to ori...
New
Qqwy
Original source of discussion: This topic on the Pragmatic Programmers’ Functional Web Development with Elixir, OTP, and Phoenix forum. ...
New
gshaw
What is the idiomatic way of matching for not nil in Elixir? E.g., First way: defp halt_if_not_signed_in(conn, signed_in_account) when...
New
pmjoe
I have a relationship of love and hate with Elixir. Lots of things are just absolutely right, but there are some things that are kind of ...
New
SoCreat
i’m a new one to elixir which editor can i use vs code? or atom? Thanks! :smiley:
New
Harrisonl
We have an ECS cluster with 4 services, where each task joins a single cluster, via discovery ECS discovery service. Currently when I de...
New

Other popular topics Top

JeremM34
Hello, how can I check the Phoenix version ? Thanks !
New
vonH
When I run the Plug and I recompile I wind up having to use Ctrl C to quit iex and start again. Witht the help of rlwrap I can use the cu...
New
joeerl
Hello again - after a longish gap I’ve decided I really must dig into Elixir and see what’s been happening here - so I have a few questio...
New
gausby
I asked this very same question on twitter and got some interesting feedback, but I thought it would be a good question to ask here as we...
1207 40165 209
New
msaraiva
Surface is an experimental library built on top of Phoenix LiveView and its new LiveComponent API that aims to provide a more declarative...
564 44265 214
New
siddhant3030
Hi, I have to write a raw query for one of my project. But till now I have used ecto queries and don’t have much experience writing raw ...
New

We're in Beta

About us Mission Statement