Команда сценария 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)
}
}