Unix脚本命令在作为外部进程启动时不捕获stdin

时间:2013-01-28 02:27:29

标签: ubuntu groovy stdin external-process

我的情况类似于Redirect Stdin and Stdout to File。我想运行一个程序并捕获运行程序的整个会话(stdin和stdout),好像它是从终端以交互方式运行的。

Dump terminal session to file我发现script命令可行。这是我的程序的简化版本(用Groovy编写),它可以做到这一点。

import groovy.io.GroovyPrintWriter

def OUTPUT_FILE = "output.txt"

def sysout = new StringBuffer()
def syserr = new StringBuffer()

// On Linux (Ubuntu 12.04) - starts the bc command with script and capture session to OUTPUT_FILE
Process proc = "script -q -f -c bc ${OUTPUT_FILE}".execute()
// On OS X with a slightly different version of Script
// Process proc = "script -q ${OUTPUT_FILE} bc".execute()

proc.consumeProcessOutput(sysout, syserr) // Spawns separate threads that will consume the out to prevent overflow

proc.withWriter { writer ->
  // Writes the following commands to the spawned process
  def gWriter = new GroovyPrintWriter(writer)
  // Imagine that the user enters these lines
  gWriter.println "a = 5"
  gWriter.println "b = 4"
  gWriter.println "c = a * b"
  gWriter.println "c"
  gWriter.println "quit"
}

proc.waitForOrKill(20000) // Give it 20 seconds to complete or kill the process

println 'Standard out captured from process:\n' + sysout
println 'Standard err captured from process:\n' + syserr

我编写的程序适用于OS X上可用的脚本版本。在OS X上,整个会话都在sysout变量中捕获,并且也正确写入OUTPUT_FILE。

以下是我希望得到的内容

bc 1.06.95
Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type `warranty'. a = 5
b = 4
c = a * b
c
20
quit

然而,这就是我在Linux上获得的(Ubuntu 12.04),它省略了输入STDIN的所有内容,只捕获了STDOUT。这是sysout和OUTPUT_FILE

的内容
bc 1.06.95
Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type `warranty'. 
20

如您所见,缺少a = 4, b = 5, etc行。

当我从命令行提示符(script)在Linux上手动运行script -q -f -c bc output.txt}命令并逐行输入时,它会起作用,即所有行也写入OUTPUT_FILE。 / p>

要让我的程序在Linux上运行,还需要执行哪些其他步骤?我知道该程序适用于OS X,因此它不太可能是程序(或Groovy)的问题。我怀疑它必须与Linux版本的脚本在作为外部进程启动时的行为方式有关。

任何指针都会受到赞赏。

1 个答案:

答案 0 :(得分:0)

我不确定为什么会在Ubuntu上而不是在OS X上发生这种情况,但是在Ubuntu上可能的解决办法可能是用你自己的类来装饰编写器,该类将写入的字符附加到StringBuffer。

它不是非常漂亮,因为您必须添加特定于操作系统的代码,以确保OS X上的输出不包含两次。

无论如何,这里是装饰程序类的完整脚本,使用Groovy 2.0.5在Ubuntu 12.04上运行正常。

import groovy.io.GroovyPrintWriter

def OUTPUT_FILE = "output.txt"

def sysout = new StringBuffer()
def syserr = new StringBuffer()

// On Linux (Ubuntu 12.04) - starts the bc command with script and capture session to OUTPUT_FILE
Process proc = "script -q -f -c bc ${OUTPUT_FILE}".execute()
// On OS X with a slightly different version of Script
// Process proc = "script -q ${OUTPUT_FILE} bc".execute()

proc.consumeProcessOutput(sysout, syserr) // Spawns separate threads that will consume the out to prevent overflow

proc.withWriter { writer ->
    // Writes the following commands to the spawned process
    def gWriter = new GroovyPrintWriter(new MyCaptureWriter(writer, sysout))
    // Imagine that the user enters these lines
    gWriter.println "a = 5"
    gWriter.println "b = 4"
    gWriter.println "c = a * b"
    gWriter.println "c"
    gWriter.println "quit"
}

proc.waitForOrKill(20000) // Give it 20 seconds to complete or kill the process

println 'Standard out captured from process:\n' + sysout
println 'Standard err captured from process:\n' + syserr

class MyCaptureWriter extends Writer {

    @Delegate OutputStreamWriter writer
    def sysout

    MyCaptureWriter(writer, sysout) {
        this.writer = writer
        this.sysout = sysout
    }

    @Override
    void write(char[] cbuf, int off, int len) throws IOException {
        sysout.append(cbuf, off, len)
        writer.write(cbuf, off, len)
    }
}