How to use executeCommandLine

executeCommandLine seems to be one of those things that trip up beginners, because people are used to execute a command as a single string that includes the command’s arguments, and it sometimes even includes a redirection or pipe.

Don’t do:

executeCommandLine("/usr/bin/curl -d 'something=\"hello world\"' https://xxx.yyy/zzz")

That won’t work.

The official way

This will work instead:

executeCommandLine("/usr/bin/curl", "-d", "something=\"hello world\"", "https://xxx.yyy/zzz")

In other words, you must split up each argument of the command, into a separate argument for executeCommandLine.

Note that you cannot have a pipe / redirection in your command here.

An easier way

A trick is to pass your entire command, even with a pipe / redirection, to your operating system shell.

On Linux/BSD/Mac:

executeCommandLine("/bin/sh", "-c", "/usr/bin/curl -d 'something=\"hello world\"' https://xxx.yyy/zzz | grep 'you can even use pipes!'")

Basically give executeCommandLine 3 arguments:

  • /bin/sh - or whatever your shell interpreter is. In Windows it’s C:/Windows/System32/CMD.EXE (note Java lets you use forward slashes instead of backslashes so you don’t have to escape the backslash with another backslash).
  • -c on *nix. For Windows use /C
  • the full command that you wanted to execute, including all its arguments, as a single string.

Note if your command contains double quotes, you’ll need to escape them with a backslash - depending on your chosen scripting language’s string literal rules.

Additional Notes

There are two versions of executeCommandLine:

  1. executeCommandLine(command): triggers the execution then immediately returns and continues to the next line of code. The command will run independently in the background. The result of the execution is not known in this version.
  2. commandOutput = executeCommandLine(Duration, command): waits until the command is finished, or the given timeout duration has elapsed, before returning the output of the command as a string. If the command takes longer, it will terminate it.

Duration is an actual java.time.Duration object.

Examples

RulesDSL

val command = "/usr/bin/curl -d 'something=\"hello world\"' https://xxx.yyy/zzz"
var result = executeCommandLine(Duration.ofSeconds(3), "/bin/sh", "-c", command)
logInfo("Exec Result", result)

JS Scripting

val command = "/usr/bin/curl -d 'something=\"hello world\"' https://xxx.yyy/zzz";
var result = actions.Exec.executeCommandLine(time.Duration.ofSeconds(3), "/bin/sh", "-c", command);
console.log(result);

JRuby

In JRuby, string literals can be expressed with single quotes (not interpolated), double quotes, or with a special % syntax which lets you use a string with both single and double quotes without having to escape it.

command = %(/usr/bin/curl -d 'something="hello world"' https://xxx.yyy/zzz)
result = Exec.execute_command_line(3.seconds, "/bin/sh", "-c", command)
logger.info(result)

JRuby also supports other ways to run a command similar to shell scripting, e.g. using backticks:

result = `/usr/bin/curl -d 'something="hello world"' https://xxx.yyy/zzz`
logger.info(result)
4 Likes

The only thing I think it’s worth adding is that without passing a Duration as the first argument, the command is “fire-and-forget”. You will receive no results from the command and the rule will not wait for the command to exit before continuing. I believe there is a timeout so the command will eventually exit but it will be long after the rule exits.

If you do pass the Duration as the first argument, executeCommandLine will block and wait for the command to complete up to the amount of time defined by the Duration and return whatever the command output to stdout/stderr (are those even a thing in Windows? :thinking: ) as a String.

Maybe also suggest that if in any case executeCommandLine is not working to post the output here in the forum

var result = actions.Exec.executeCommandLine(time.Duration.ofSeconds(3), …);
console.log(result);

There are a lot of questions where the OP isn‘t really aware of how to get back the resulting output.

1 Like

Thanks for the feedback @rlkoshak and @Oliver2. I have added additional notes at the end and some examples for each language. I’ll add Blockly too in a bit.

1 Like

I really had to smile about this.
So easy and human-language-alike. Instead of time.Duration.ofSeconds(3):grinning:

1 Like

There’s also 3.seconds.ago and 3.seconds.from_now which returns a ZonedDateTime.

Just for sake of discussion, time.Duration.of() also supports ISO8601 duration Strings so if you have a mix of units (e.g. minutes and seconds) one can use time.Duration.of("PT1M5S"). Maybe not as concise as the Ruby by better than time.Duration.ofMinutes(1).plus seconds(5). Put days (D), weeks (W), and years before the “T”.

This works anywhere the Java Duration is used (e.g Groovy, Rules DSL), as well as in JS Scripting.

I’m not sure if Blockly has a Duration block.

This topic was automatically closed 41 days after the last reply. New replies are no longer allowed.