I'm trying to interact with sh
. This is the easiest case I want to resolve.
typealias ExitStatus = Int
fun executeIO(cmd: List<String>): ExitStatus =
ProcessBuilder(cmd).inheritIO().start().waitFor()
But the Kotlin code doesn't have any control when the sh
are executing.
fun executeNTimes(cmd: List<String>, times: Int) = runBlocking {
val process = ProcessBuilder(cmd)
.start()
launch { process.errorStream.bufferedReader().useLines { seq -> seq.toList().forEach { println("stderr: $it") } } }
launch { process.inputStream.bufferedReader().useLines { seq -> seq.toList().forEach { println("stdout: $it") } } }
OutputStreamWriter(process.outputStream, "UTF-8").use { w ->
repeat(times) {
readln().let { println("input: $it"); w.write(it) }
w.appendLine()
w.flush()
}
}
process.waitFor()
}
But that is not interactive!
cmd = sh and times = 2:
echo exit on stdout
input: echo exit on stdout
echo exit on stderr 1>&2
input: echo exit on stderr 1>&2
stderr: exit on stderr
stdout: exit on stdout
Is not interactive because needs to close the buffer for starting to work.
My expectation for interactive process is the next one:
input: echo exit on stdout
stdout: exit on stdout
input: echo exit on stderr 1>&2
stderr: exit on stderr
input: exit
How I can do that?
After read:
The code has:
fun executeExpect() {
val process = ProcessBuilder(listOf("sh")).start()
val expect = ExpectBuilder()
.withInputs(process.inputStream, process.errorStream)
.withOutput(process.outputStream)
.build()
fun send(str: String) {
expect.sendLine(str)
println("input: $str")
}
"echo exit on stdout".let(::send)
expect.expect(regexp("\n$")).before.let { println("stdout: $it") }
"echo exit on stderr 1>&2".let(::send)
expect.expectIn(1, regexp("\n$")).before.let { println("stderr: $it") }
"exit".let(::send)
expect.expect(eof());
process.waitFor()
expect.close()
}
And now the output is:
input: echo exit on stdout
stdout: exit on stdout
input: echo exit on stderr 1>&2
stderr: exit on stderr
input: exit