The Crystal Programming Language Forum

Are Process arguments overcomplicated?

Imagine, we have a tuple of command argument we want to run with Process. Currently, we’ll have to do something like:

args = {"ffprobe",
  "-v", "error", "-show_entries", "format=duration",
  "-of", "default=noprint_wrappers=1:nokey=1", file}

ary_args = args.to_a # intermediate array

Process.run(ary_args.shift, ary_args)

But it feels more natural to do it like:

args = {"ffprobe",
        "-v", "error", "-show_entries", "format=duration",
        "-of", "default=noprint_wrappers=1:nokey=1", file}

Process.run args
# or
Process.run *args

Even with array of arguments it feels more handy to do it like:

args = %w[ffprobe -v error -show_entries format=duration
        -of default=noprint_wrappers=1:nokey=1] << file
# ^^^ have to shovel (Array#<<) into the array, since we don't have interpolated arrays %W[]

Process.run args

There probably was a reason for choosing the current format, but does it still stand?
What do you think?

2 Likes

Seems reasonable to me…

The current version is better because it does not rely on the filename being in the correct position in an array as function parameter.

Also, I’m curious why you don’t use a literal Array instead of the Tuple? [] instead of {} in the first example?

Could you demonstrate what you mean, please? I think it’s common to build an argument list somewhere and then pass the result to run the prepared command. Schematically, it could be represented like this:

def build_command_arguments
  # ...
end

def run_command
  command_arguments = build_command_arguments
  Process.run command_arguments
end

Can the current version be beneficial in that case?

I use Tuple whenever it’s possible because Tuple is cheaper than Array.

In this:
Process.run command_and_arguments
“command_and_arguments” contains the command to run and the arguments for that command, and the command that will be executed has to be in a defined position in the array. It can’t be anywhere in the array.
I don’t find that very intuitive nor easily readable.
Also, just because some pattern is common in some programming language, does not mean it needs to be present in any language, or be solvable in the same way in any language.
From a semantic point of view, the command to run is simply not the same as the arguments to that command. Especially since the arguments can (and often are) even omitted.

Oh, if my guess is right, what you are trying to say is that currently one can write it like this:

Process.run args: {"arg1", "arg2"}, command: "command"

Is that it?

1 Like

Well, thats one way to use it and is certainly more readable, yes.

There are basically two modes of how to use Process.run. Either with program name as string and arguments as an array, or the entire command as a string (with shell: true; additional arg array is optional).

You shouldn’t do that. The difference is not really worth much unless the code runs really often and puts a lot of stress on the GC. When running a program, you should just directly use an array, not a tuple. This works out of the box and doesn’t add additional complexity Process.run needs to build an array anyway, so you don’t gain anything.

Process.run *args

might be nice. Or even Process.run(args) I think it’s a common pattern. Maybe

Process.run args[0], args[1..-1]

Doesn’t seem too bad?