os/exec and bytes.Buffer clarity

blov · · 470 次点击    
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
<p>I was looking at the os/exec package and I found myself a little confused with the example for exec.Command and the use of the bytes.Buffer.</p> <p>The example in the documentation is throwing me off a little as I&#39;m not really understanding how the output of cmd.Run is put into the buffer variable.</p> <p>Example from the docs:</p> <pre><code>package main import ( &#34;bytes&#34; &#34;fmt&#34; &#34;log&#34; &#34;os/exec&#34; &#34;strings&#34; ) func main() { cmd := exec.Command(&#34;tr&#34;, &#34;a-z&#34;, &#34;A-Z&#34;) cmd.Stdin = strings.NewReader(&#34;some input&#34;) var out bytes.Buffer cmd.Stdout = &amp;out err := cmd.Run() if err != nil { log.Fatal(err) } fmt.Printf(&#34;in all caps: %q\n&#34;, out.String()) } </code></pre> <p>Is this because stdout is being assigned the value of the pointer to &#34;out&#34;? That seems backwards to me. I tested it with my own code and it works I&#39;m just not understanding how I guess, and the docs are a little confusing to me to read.</p> <hr/>**评论:**<br/><br/>megadyne: <pre><p>cmd.Stdout is just an io.Writer. bytes.Buffer <a href="https://golang.org/pkg/bytes/#Buffer.Write" rel="nofollow">implements Write</a>. Everything written to cmd.Stdout, will be written to the underlying io.Writer, in this case the bytes.Buffer. Or if you choose an ordinary file or a network connection (net.Conn expects an implementation of io.Writer aswell).</p> <p>If the command (e.g. <strong>tr</strong>) is executed the result from normal Stdout has to be written somewhere. For example you can discard the result to <strong>/dev/null</strong></p> <pre><code>echo &#34;some input&#34; | tr &#34;a-z&#34; &#34;A-Z&#34; &gt; /dev/null </code></pre> <p>but in the case of cmd.Run(), the data will be written to the given io.Writer. This could be just play os.Stdout out again, or here a bytes.Buffer. After the result was written to the bytes.Buffer you can just read it out with String(), Bytes(), or copy it to Stdout again with</p> <pre><code>out.WriteTo(os.Stdout) </code></pre></pre>planet12: <pre><p>I suspect the OP&#39;s confusion is because cmd.Stdout an interface, and an interface must be filled by a pointer, not a value - hence the:</p> <pre><code>cmd.Stdout = &amp;out </code></pre></pre>jerf: <pre><p>Non-pointer values can implement interfaces. The popular Context object works that way. Since you can&#39;t modify them via the interface methods, it makes them the closest things to immutable values there are in Go.</p></pre>planet12: <pre><p>Write in haste, repent at leisure :)</p> <p>If your type implements all the interface methods with value receivers, you can indeed assign a value directly to an interface type variable, eg.</p> <pre><code>type ValueWriter struct{} func (n ValueWriter) Write([]byte) (int, error) { return 0, nil } var w io.Writer = ValueWriter{} </code></pre> <p>This is not the case for most (all?) io.Writer&#39;s I&#39;ve seen though, which use a pointer receiver, eg. bytes.Buffer&#39;s write method:</p> <pre><code>func (b *Buffer) Write(p []byte) (n int, err error) { ... } </code></pre> <p>FYI all the standard context.Context types use pointer receivers.</p></pre>Dangle76: <pre><p>My confusion comes from the fact that to me this says cmd.Stdout takes the value of out, when the description you&#39;re all putting forth is that you&#39;re dumping the value of stdout onto outs memory location </p></pre>megadyne: <pre><p><code>cmd.Stdout</code> is just a Field in <a href="https://golang.org/pkg/os/exec/#Cmd" rel="nofollow">type Cmd struct</a>. </p> <p>And <code>cmd.Stdout</code> doesn&#39;t take the value of <code>out</code>, it takes the pointer to <code>out</code>. This is necessary because <code>Write()</code> is defined on <code>(b *Buffer)</code>.</p> <p>Later down in the execution of <code>cmd.Run()</code> the <code>io.Writer</code>s in <code>type Cmd struct</code> are handed off to <a href="https://golang.org/pkg/os/#StartProcess" rel="nofollow">StartProcess</a> in the <a href="https://golang.org/pkg/os/#ProcAttr" rel="nofollow">ProcAttr</a>. The first 3 values of the Field <code>Files</code> are used as Stdin, Stdout and Stderr. </p> <p>In the case of <code>cmd.Run()</code> it will be <code>cmd.Stdin</code>, <code>cmd.Stdout</code> and <code>cmd.Stderr</code> in the Field <code>Files</code> of <code>attr *ProcAttr</code> (they are wrapped a bit, to satisfy the interface). <code>StartProcess()</code> doesn&#39;t know it is just a <code>*bytes.Buffer</code>, it only knows it can write to a <code>*File</code>, which also <a href="https://golang.org/pkg/os/#File.Write" rel="nofollow">implements</a> <code>io.Writer</code>.</p> <blockquote> <p>you&#39;re all putting forth is that you&#39;re dumping the value of stdout onto outs memory location </p> </blockquote> <p>No, we are saying that <code>bytes.Buffer</code> implements the interface <code>io.Writer</code> and every underlaying functions can call <code>cmd.Stdout.Write([]byte(&#34;YOUR DATA HERE&#34;))</code> and the data will be written to whatever implementation of <code>io.Writer</code> <code>cmd.Stdout</code> currently has.</p></pre>Dangle76: <pre><p>So just to ensure I&#39;m understanding here, the field is taking the pointer to the bytes.Buffer, and upon executing run, stdout is being written to that memory location, which (in the example case) is what out is also accessing as a bytes.Buffer?</p></pre>

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

470 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传