ask /r/golang: a way to prevent flag from printing usage when there's an error?

blov · · 680 次点击    
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
<p>By default, if you leave off an argument, the standard &#34;flag&#34; package will print an error, and then print the programs entire usage.</p> <p>For programs with long usage, I find this pretty unhelpful, because the error is hard to see, way up above all the usage. You&#39;re left sitting there wondering why the program suddenly decided to spit its usage at you.</p> <p>I&#39;d much rather it just spit out exactly the error, and then if you&#39;re still confused, you can run with -h to see the full story.</p> <p>Is there any way to inhibit printing usage on error? I looked through the package, but didn&#39;t see anything.</p> <p>TIA!</p> <hr/>**评论:**<br/><br/>avolkhin: <pre><p>You can redefine flag.Usage function to whatever fits your needs.</p></pre>dilap: <pre><p>Right, but that&#39;s not my problem -- my problem is that on error, I don&#39;t want to barf out all the usage (which obscures the error), I want to just print the error. The user can print the usage themselves if the error resulted from confusion.</p></pre>avolkhin: <pre><p>Oh, I see. Then I think you need to redefine FlagSet.failf method to just printing the error. Somthing like this:</p> <pre><code>type QuiteFlagSet struct { *flag.FlagSet } func (f *QuiteFlagSet) failf(format string, a ...interface{}) error { err := fmt.Errorf(format, a...) fmt.Fprintln(os.Stderr, err) return err } </code></pre></pre>dilap: <pre><p>Yup, that&#39;s the idea. But would that actually work? When FlagSet calls failf, it&#39;ll call it&#39;s own failf method, not the QuiteFlagSet one, right?</p> <p>So it seems like I&#39;d need to just use my own version of the whole flag library, which is definitely too much trouble for such a little thing.</p> <p>Hmm I wonder if there&#39;s some way to detect we&#39;re failing in the usage and inhibit printing it... Ah that would work I guess, just set a variable after we&#39;ve parsed the flags successfully. </p> <p>Oh wait, no because then -h doesn&#39;t work! Aughhh. :)</p></pre>tagesticket: <pre><p>The simplest way to do this would be to use a FlagSet and redirect its output to a buffer of your choice. This allows you to capture any usage that is printed out and control when it is printed. You can also redefine the Usage method in addition to this.</p> <p>See <a href="http://play.golang.org/p/LG7Jxc8M_1" rel="nofollow">http://play.golang.org/p/LG7Jxc8M_1</a></p> <pre><code>package main import ( &#34;bytes&#34; &#34;flag&#34; &#34;fmt&#34; ) func parseFlags(args []string) (usage string, err error) { s := &amp;flag.FlagSet{} // Define flags var tag string s.StringVar(&amp;tag, &#34;tag&#34;, &#34;&#34;, &#34;Example flag&#34;) // Error output buffer buf := bytes.NewBuffer([]byte{}) s.SetOutput(buf) err = s.Parse(args) if err != nil { return string(buf.Bytes()), err } return &#34;&#34;, nil } func main() { usage, err := parseFlags([]string{&#34;--helpee&#34;}) if err != nil { fmt.Println(&#34;Usage Output: &#34;, usage) fmt.Println(&#34;Error:&#34;, err) } } </code></pre></pre>dilap: <pre><p>Interesting. Doesn&#39;t work with the built-in default flag set flag.CommandLine, though, because that&#39;s set to exit on error.</p> <p>Oh, well. Definitely seems to be swimming against the flag current here, so probably not worth any gymnastics.</p> <p>Edit:</p> <p>OK, I think the cleanest thing you can do to get this for the default flag set is (1) define your own -h flag, (2) set usage to func() {} and (3) call flag.CommandLine.Parse yourself. Seems on the edge of worth it.</p></pre>tagesticket: <pre><p>You could always use a custom FlagSet and then call yourFlagSet.Parse(os.Argv) to get the behaviour you want.</p></pre>H3g3m0n: <pre><p>I had to do something similar with docopts which always prints the help message even if you tell it not to. You can swap out the output stream befoure and after the function runs:</p> <pre><code>tmp := os.Stdout os.Stdout = nil defer { os.Stdout = tmp } someBadLibraryThatDoesntTakeAnioWriter() </code></pre> <p>Not thats off the top of my head since my cpu died, now I&#39;m on a raspberrypi2 and I don&#39;t have access to my code. If nil doesn&#39;t work there is, os.Pipe or some other dummy writer. Also there is os.Stderr.</p> <p>It&#39;s also useful for testing as you can stick in a bytes.Buffer as a memwriter and capture the output.</p> <p>Just be careful, it&#39;s not threadsafe as os.Stdout is a global variable. If you use Fprintf/Fprintln though out your program (or at least in any threads) and feed in your own io.Writer (which could probably just be a copy of the origional os.Stdout, although I can&#39;t guarentee the thready safety of os.Stdout itself) for output then it shouldn&#39;t be an issue.</p> <p>Personally I never use a normal Printf/Println, instead use fmt.Fprintln for everything and make sure my libraries all take in a io.Writer. I highly recomend all library authors do the same. Otherwise there is the log.Logger or whip up some custom logger interface that clients can implement.</p> <p>On a similar not there is afero.Fs for filesystems. Lets you stick in a memory writer, or http, etc...</p></pre>

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

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