Useful predicate: run_process/3

I’ve written a predicate to run a process and capture its stdout or stderr a million times, today I just ran into the fact it is built in in SWI: run_process/3. For some reason the docs are not linked directly, but here is the link: build_tools:run_process/3

?- use_module(library(build/tools)).
true.
?- run_process(path(ls),['/usr/bin/ls'],[output(Out),error(E)]).
Out = `/usr/bin/ls\n`,
E = [].
2 Likes

The build/X libraries are intended as support libraries for pack_rebuild/1. I guess they won’t disappear. It is not clear to me what the status of such support libraries should be though.

Unlike the other predicates there, run_process/3 seems to be more generally useful. Is it an option to lift it to library(process) or another officially supported library?

2 Likes

If you are only interested in Out, there is also

?- use_module(library(by_unix)).

?- Out @@ ls( ‘/usr/bin/lso’ ).
Out = [‘/usr/bin/ls’].

even,

?- Out @@ upsh(say,tara).
% /home/nicos/.local/share/swi-prolog/pack/upsh/scripts/say.pl compiled 0.00 sec, 6 clauses
Out = [tara].

just for fun,

Nicos Angelopoulos

https://stoics.org.uk/~nicos

If this goes into a standard library, it’d be nice if it outputs a string rather than a list of codes; and possibly have an option lines(true), to automatically run string_lines/2 on the output.

An exhaustive list of useful options can be found by looking at the Python subprocess module.

2 Likes

On the other hand, code lists are good because they can be parsed right away. I guess that can be an option, too.

My initial proposal would have been easy enough to do even for me, if @jan is OK with it, namely: just take this as it is right now and move it to an officially supported library. When I looked through the definition, it seems to use standard library predicates but does not depend on anything else within library(build_tools).

I’m not so sure. library(process) is a quite closely compatible library to its SICStus Prolog cousin. I would like to keep it that way. There are zillions of ways to implement more high level libraries from there. This tends to get incompatible though as one in general either need threads or something similar to POSIX select to handle multiple I/O streams from a single thread. For example, library(git) also implements some abstractions. The tradeoff is a little hard. On the one hand there are many possible use cases with different demands, most of which are implemented with just a short amount of code. On the other hand there are many ways to do it wrong, notably leak resources (file handles notably) or get into deadlocks because the process cannot write due to a full output pipe.

Does this ask for a library or just example code that can be tweaked by projects?

Hi Jan, thank you for the detailed answer. I see your point. My take on this is that this does ask for a library that covers some common use cases and as such provides example code on how to do it right. If I find time and motivation I would try to read through the available examples at the moment and see how such a library might look like from the client point of view.

So if I understand correctly, there is, at the moment, at least:

run_process/3 in library(build_tools)
git/3 and others in library(git)

I can look for uses of library(process) to find other examples.

Agree. swish also contains examples using graphviz. As this uses a lot of data we have to be careful about deadlocks due to full buffers. If I recall well it creates a thread that dynamically generates the input for graphviz and than reads the output.

Considering the status of library(process) I do insist on a new library as part of the same package. Finding a good name is another challenge :slight_smile:

Maybe there is another side to the coin for staying similar to SICStus Prolog library(process) in that instead of not adding the predicate to SWI-Prolog that SiCStus consider adding it to their library and then SWI-Prolog can also add it. :slightly_smiling_face: