问题
I'm porting a Bash shell script to Groovy. Most constructs can easily be converted (e.g. mkdir "$foo"
to foo.mkdir()
. However, I'm stumped on this:
#!/bin/bash
sleep 60 > /tmp/output.log 2>&1 < /dev/null
When running it, let's inspect the file descriptors of sleep
:
$ ls -l /proc/$(pgrep sleep)/fd
total 0
lr-x------ 1 user user 64 Feb 25 13:40 0 -> /dev/null
l-wx------ 1 user user 64 Feb 25 13:40 1 -> /tmp/output.log
l-wx------ 1 user user 64 Feb 25 13:40 2 -> /tmp/output.log
Running a process in Groovy can be done this way (according to this page):
#!/usr/bin/groovy
def log = new FileOutputStream("/tmp/output.log")
def sleep = "sleep 60".execute()
sleep.waitForProcessOutput(log, log)
And the file descriptors of sleep
:
$ ls -l /proc/$(pgrep sleep)/fd
total 0
lr-x------ 1 user user 64 Feb 25 13:41 0 -> pipe:[522455]
l-wx------ 1 user user 64 Feb 25 13:41 1 -> pipe:[522456]
l-wx------ 1 user user 64 Feb 25 13:41 2 -> pipe:[522457]
As can be seen, the file descriptors tie to something else (probably the Groovy process). Because this will be used for a long-running process, I'd like to cut out Groovy as a middle man.
So, my question: how do I redirect a file to stdin
and stdout
and stderr
to files, such that the external process can be detached and Groovy does not need to be running?
EDIT: This question is not a duplicate of capture process output in Groovy, because that question concerns redirecting stdout
and stderr
to the stdout
and stderr
of the Groovy process itself. Which, as can be seen by @tim_yates` answer, is quite a different thing.
回答1:
ProcessBuilder.redirectOutput()
can solve this problem since Java 7. And because it's standard Java, it can also be used in Groovy.
#!/usr/bin/groovy
def sleep = new ProcessBuilder('sleep', '60')
.redirectOutput(new File('/tmp/output.log'))
.redirectErrorStream(true)
.redirectInput(new File('/dev/null'))
.start();
The result:
$ ls -l /proc/$(pgrep sleep)/fd
total 0
lr-x------ 1 user user 64 Feb 26 11:44 0 -> /dev/null
l-wx------ 1 user user 64 Feb 26 11:44 1 -> /tmp/output.log
l-wx------ 1 user user 64 Feb 26 11:44 2 -> /tmp/output.log
ProcessBuilder.start()
returns a java.lang.Process
, which is decorated by Groovy. Methods such as waitForOrKill
will still work.
回答2:
Why not just leverage the shell? Let it do what it was designed to do. To wit:
#!/usr/bin/groovy
def sleep = (["sh", "-c", "sleep 60 > /tmp/output.log 2>&1 < /dev/null"] as String[]).execute()
If you need to specify the output file programmatically, then just use a GString:
#!/usr/bin/groovy
def outfile = "/tmp/output.log"
String[] sleepCmd = ["sh", "-c", "sleep 60 > ${outfile} 2>&1 < /dev/null"]
def sleep = sleepCmd.execute()
(Edited both examples to use String[]
rather than String
)
来源:https://stackoverflow.com/questions/28717454/how-do-i-redirect-a-process-stdout-stderr-and-stdin-to-and-from-files-in-groov