Execute (real) shell commands from Groovy.
September 26, 2012 | comments |This post is about running shell commands from within Groovy, specifically bash but it is easy to adapt to other shells. You can already run commands with syntax like:
"ls -l".execute()
That is about as simple as it gets and works great for many situations. However, execute() runs the given command passing it the list of options, the options are NOT passed through the shell (e.g. bash) for expansion and so on. As a result, you can NOT do something like:
"ls *.groovy".execute()
In this case, no shell sees the * to expand it, and so it just gets passed to ls exactly as it is. To address this, we can create a shell process with ProcessBuilder and pass the command to the shell for execution. A common use case for me is to want to just pipe the shell command's output to stdout. With some Groovy meta-object programming we can make this a method of GString and String so that you can execute any kind of string simply by calling, for example, a .bash() method on the string. Below is a class that does that. This class (including improvements) is included in durbinlib.jar. With this class, one can not only properly execute the ls *.groovy example above, but can even execute shell scripts like:
"""
for file in \$(ls);
do
echo \$file
done
""".bash()
To turn on this functionality it is necessary to call RunBash.enable() first. So a full example using the durbinlib implementation is:
#!/usr/bin/env groovy
RunBash.enable()
"""
for file in \$(ls);
do
echo \$file
done
""".bash()
A skeleton of the class itself follows:
{
static boolean bEchoCommand = false;
// Add a bash() method to GString and String
static def enable(){
GString.metaClass.bash = {->
RunBash.bash(delegate)
}
String.metaClass.bash = {->
RunBash.bash(delegate)
}
}
static def bash(cmd){
cmd = cmd as String
// create a process for the shell
ProcessBuilder pb = new ProcessBuilder("bash", "-c", cmd);
pb.redirectErrorStream(true); // use this to capture messages sent to stderr
Process shell = pb.start();
shell.getOutputStream().close();
InputStream shellIn = shell.getInputStream(); // this captures the output from the command
// at this point you can process the output issued by the command
// for instance, this reads the output and writes it to System.out:
int c;
while ((c = shellIn.read()) != -1){
System.out.write(c);
}
// wait for the shell to finish and get the return code
int shellExitStatus = shell.waitFor();
// close the stream
try {
shellIn.close();
pb = null;
shell = null;
} catch (IOException ignoreMe) {}
}
}Labels: bash, groovy, ProcessBuilder
