<p>I am trying to spawn a PowerShell process and then use the stdin/out/err pipes to send commands and receive their output.</p>
<pre><code>cmd := exec.Command("powershell.exe", "-NoExit", "-Command", "-")
stdin, _ := cmd.StdinPipe()
stdout, _ := cmd.StdoutPipe()
cmd.Start()
stdin.Write([]byte("$a = 1\r\n"))
ioutil.ReadAll(stdout)
stdin.Write([]byte("$b = 2\r\n"))
ioutil.ReadAll(stdout)
stdin.Write([]byte("$a + $b\r\n"))
ioutil.ReadAll(stdout)
</code></pre>
<p>Unfortunately, it seems I must call <code>.Close()</code> on the stdin pipe before the command is actually send and I can read stdout. Obviously the second <code>.Write()</code> attempt then fails flat on its face.</p>
<p>Is it possible to send multiple inputs into a process? I've found a <a href="https://groups.google.com/forum/#!topic/golang-nuts/KYfp4uh9mvk" rel="nofollow">golang-nuts thread</a> from 2013, but that ended without a solution.</p>
<p>[Edit: Here is a minimal compiling sample: <a href="http://pastebin.com/MNTeyrGt" rel="nofollow">http://pastebin.com/MNTeyrGt</a>]</p>
<hr/>**评论:**<br/><br/>Matthias247: <pre><p>Your problem is that you try to read the stdout with ioutil.ReadAll. This will block until stdout produces end of stream. However as you might imagine that a shell will normally not close it's output until the shell is closed: It has to read new commands (through stdin) and provide the output for them (via stdout). So after writing a command you should not read up to EndOfStream but only up to the point where the shell expects a new input. That's however not too easy, as some commands will output only a single line and others multiple ones - and it may even take some time until all output is produced.</p></pre>xrstf: <pre><p>Thanks for your input; it kind of confirms my thoughts that I simply cannot just use <code>ReadAll()</code> in this case, but have to handle reading from the stream myself.</p>
<p>I got inspired by jPowerShell, a Java wrapper around PS, to handle the end-of-output by always including an <code>...; echo random_marker_string</code> to the command and then scanning for that string.</p>
<p>[Edit: So I've changed my code to not close stdin and manually read a few bytes from stdout, like so</p>
<pre><code>func run(stdin io.WriteCloser, stdout io.ReadCloser, command string) (string, error) {
fmt.Printf("> %s\n", command)
written, err := stdin.Write([]byte(command + "\r\n"))
if err != nil {
return "", err
}
buf := make([]byte, 128)
read, err := stdout.Read(buf)
fmt.Printf("read %d bytes from stdout.\n", read)
if err != nil {
return "", err
}
return string(buf), err
}
</code></pre>
<p>This works better, but it also clearly brings up a bunch of timing issues. Like waiting for 2 seconds after writing to stdin before reading from stdout works much better. There's a lot of work to do, but at least it seems possible after all :-)]</p></pre>jammerlt: <pre><p>Did you try to flush the writes?</p></pre>xrstf: <pre><p>The stdin pipe is an <a href="https://golang.org/pkg/io/#WriteCloser" rel="nofollow"><code>io.WriterCloser</code></a> and supports only <code>.Write</code> and <code>.Close</code>, if I'm not mistaken.</p></pre>jammerlt: <pre><p>I think you can type assert it to a io.Flusher, and call flush, same like you can on a http.ResponseWriter</p></pre>xrstf: <pre><p>The value returned by <code>StdinPipe()</code> is an <a href="https://golang.org/src/os/exec/exec.go#L522" rel="nofollow"><code>exec.closeOnce</code></a> struct, which does not support <code>Flush()</code>. I could try to gain access to the embedded <code>os.File</code>, but that would still not give me a flush, only a <code>Sync()</code>.</p></pre>ChristophBerger: <pre><p>Both <code>io.Writer.Write</code> and <code>ioutil.ReadAll</code>return an error as the second return value. Always check returned error values, they help determining why a call fails.</p></pre>xrstf: <pre><p>Sorry for providing a misleading sample; my code does indeed check for any error that occurs. :)</p>
<p>I've created a minimal running sample at <a href="http://pastebin.com/MNTeyrGt" rel="nofollow">http://pastebin.com/MNTeyrGt</a></p>
<p>Without the <code>Close()</code>, <code>ReadAll()</code> just hangs and no errors occur anywhere. With closing, the second attempt to write to stdin results in a</p>
<pre><code>> echo foo
~ wrote 10 bytes to stdin.
foo
> echo bar
panic: write |1: Das Handle ist ungültig.
goroutine 1 [running]:
panic(0x4c5d20, 0xc042096b10)
D:/opt/go/src/runtime/panic.go:500 +0x1af
main.main()
Q:/gospace/src/github.com/xrstf/testps/main.go:35 +0x2ca
</code></pre></pre>ChristophBerger: <pre><p>Ok, I believe <a href="/u/Matthias247" rel="nofollow">/u/Matthias247</a> nailed the problem. ReadAll waits indefinitely, and when not reading all, it is difficult to know when to stop. </p>
<p>If you just want to collect all output (e.g. for logging purposes), you could read the output in a separate goroutine.</p>
<p>If you need the output from one shell command for invoking the next one, this trick might help:</p>
<p>After each command, send an echo command (or whatever PowerShell's equivalent to Bash echo is) with a unique text, e.g. <code>echo ***end***</code>. Then read stdout up to this text - then you know you have captured all of the recent command's output.</p></pre>sbinet: <pre><p>not sure whether powershell works exactly like bash, but here what one should do in that situation: use <code>pkg/term</code>.</p>
<p><em>e.g.</em>: <a href="https://github.com/driusan/gosh/blob/master/main.go#L226" rel="nofollow">https://github.com/driusan/gosh/blob/master/main.go#L226</a></p></pre>
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889
- 请尽量让自己的回复能够对别人有帮助
- 支持 Markdown 格式, **粗体**、~~删除线~~、
`单行代码`
- 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
- 图片支持拖拽、截图粘贴等方式上传