2

My situation is similar to Redirect Stdin and Stdout to File. I want to run a program and capture the entire session (stdin and stdout) of running a program as though it had been run interactively from a terminal.

From Dump terminal session to file I found out that the script command would work. Here is a simplified version of my program (written in Groovy) that does this.

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

The program I wrote works on the version of script available on OS X. On OS X, the entire session is captured in the sysout variable and also written to the OUTPUT_FILE correctly.

Here's what I expect to get

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

However, here is what I get on Linux (Ubuntu 12.04), it omits everything that was typed into STDIN and only captures STDOUT. Here is the content of sysout and 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

As you can see, the lines a = 4, b = 5, etc are missing.

When I run the script command manually on Linux from the command line prompt (script -q -f -c bc output.txt} and enter the lines one by one, it works, i.e., all the lines are also written to OUTPUT_FILE.

What additional steps do I need to do to get my program to work on Linux? I know that the program works on OS X so it is unlikely to be a problem with the program (or Groovy). I suspect that it must be something with how the Linux version of script behaves when launched as an external process.

Any pointers would be appreciated.

4

1 回答 1

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)
    }
}
于 2013-02-28T19:13:01.490 回答