Команда сценария Unix не захватывает стандартный ввод при запуске как внешний процесс

Моя ситуация похожа на перенаправить стандартный ввод и стандартный вывод в файл. Я хочу запустить программу и записать весь сеанс (stdin и stdout) запуска программы, как если бы она была запущена в интерактивном режиме с терминала.

Из сеанса терминала Dump в файл я узнал, что 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.

Какие дополнительные шаги мне нужно сделать, чтобы моя программа работала в Linux? Я знаю, что программа работает на OS X, так что вряд ли это проблема с программой (или Groovy). Я подозреваю, что это должно быть что-то с тем, как ведет себя версия скрипта для Linux, когда запускается как внешний процесс.

Любые указатели будут оценены.

1 ответ

Я не уверен, почему это происходит в Ubuntu, а не в OS X, но один из возможных способов обойти Ubuntu может заключаться в том, чтобы украсить писателя вашим собственным классом, который добавляет написанные символы в StringBuffer.

Это не очень красиво, так как вам придется добавить специфичный для ОС код, чтобы убедиться, что вывод не включен дважды в OS X.

В любом случае, вот сценарий полностью с классом decorator, который отлично работает для меня на Ubuntu 12.04 с использованием Groovy 2.0.5.

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)
    }
}
Другие вопросы по тегам