tmux invocation works on command line but not in exec.Command.Run() - would love some help

xuanbao · · 510 次点击    
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
<p>I&#39;m building a tmux invocation that results in:</p> <pre><code>tmux new -s tmux-session-name &#39;java -jar server.jar&#39; </code></pre> <p>This works <em>perfectly well</em> when run from the command line, but for whatever reason, when I run it from exec.Command.Run() it is treating it like it doesn&#39;t have quotes. I have tried both single and escaped double quotes.</p> <p>What am I missing?</p> <hr/>**评论:**<br/><br/>TheMerovius: <pre><p>a) do you set stdin and stdout correctly? (namely, to a terminal) b) post your code. But <code>cmd := exec.Command(&#34;tmux&#34;, &#34;new&#34;, &#34;-s&#34;, &#34;tmux-session-name&#34;, &#34;jar -jar server.jar&#34;)</code> (with stdout and stdin set appropriately) should work. In particular, you need to pass the whole quoted string as <em>one</em> string.</p></pre>nathanpaulyoung: <pre><p>I did cmd.stdin = os.stdin for a while, along with err and out, but I didn&#39;t seem to notice it DOING anything.</p></pre>nathanpaulyoung: <pre><p>Ohhhhhh. Fuckin&#39; weird! Why is that? I mean, I can do that just fine, but why? I&#39;m guessing because that&#39;s something similar to a single argument &#34;slot&#34;?</p></pre>TheMerovius: <pre><p>Think of it this way: shell is a programming language, the syntax for calling a function is <code>funcname arg1 arg2 arg3</code> and if you need spaces in one of your arguments you need to either quote them (with \) or quote them (which is similar to a raw string literal in go, i.e. it ignores shell meta-characters like spaces). Functions are then programs and above function call translates into the syscall (roughly) <code>execl(funcname, funcname, arg1, arg2, arg3)</code> (the first funcname is the program to execute and the second one is argv[0], which usually contains the name of the program).</p> <p>Thus, your line of shell code creates the call <code>execl(&#34;tmux&#34;, &#34;tmux&#34;, &#34;new&#34;, &#34;-s&#34;, &#34;tmux-session-name&#34;, &#34;java -jar server.jar)</code>. Go is a translation of that.</p> <p>This is the general difference between using a shell-like process creation (like with <code>system</code> in C) and an exec-like process creation. You never want to use the former and always the latter, as it is a) simpler (because you don&#39;t have to worry about escaping stuff) and b) safer and more secure (because it is pretty darn hard to safely escape arbitrary strings).</p></pre>nathanpaulyoung: <pre><p>I followed all the way up to tmux tmux. Where is the second one coming from?</p></pre>TheMerovius: <pre><p>The process gets passed an array of arguments (in C usually called argv, in go it&#39;s in os.Args). By convention, the first one (index 0) is the name of the binary being called (though that&#39;s just a convention, it&#39;s not actually enforced. Both the shell and go&#39;s exec package follow it, though). That is why you pass it twice: The first one is the binary to be executed (this <em>must</em> be the binary name, because the syscall needs to find it to execute it). The following arguments are the arguments passed in argv, so you start it with the binary name (following convention).</p> <p>In go, if you do <code>exec.Command</code>, it will <a href="https://golang.org/src/os/exec/exec.go#L125" rel="nofollow">automatically populate argv appropriately</a>.</p> <p>All of this becomes much clearer once you actually did some systems programming :)</p></pre>nathanpaulyoung: <pre><p>Okay, I think there&#39;s some misunderstanding here. My program is invoked like &#34;msct start name-of-server&#34;. msct will then use name-of-server as the one and only entry in os.Args and I extract that for use with os.Args.First(). I have a function hardcoded to use the name-of-server argument and info from my config file to build out the full set of arguments to pass. The arguments start with &#34; new&#34; (passed to tmux) and ends with the location of the jar file for java to run. These are then passed back to the handler for the &#34;start&#34; command at program invocation, which contains &#34;cmd := exec.Command(&#34;tmux&#34;, args...)&#34; followed later by the actual .Run() call.</p> <p>Sorry for formatting. I am on mobile.</p></pre>TheMerovius: <pre><p>No misunderstanding, I was just trying to explain why you need to pass the java string as a single string if you want the equivalent invocation to your shell-line.</p> <p>Your problem is <a href="https://github.com/nathanpaulyoung/msct/blob/master/msct.go#L142" rel="nofollow">here</a>. As I explained, the quotation marks in your shell-line aren&#39;t part of the exec-call, but they are part of the shell programming language syntax. You need to pass that as one argument, approximately like this:</p> <pre><code> args = append(args, fmt.Sprintf(&#34;java -server -ms%dM -Xmx%dM %s -jar %s&#34;, ram, ram, strings.Join(javaParamsArray, &#34; &#34;), fullpath)) </code></pre> <p>(untested, of course. And you probably want that more readable). I tried to explain that your shell-line <code>tmux new -s tmux-session-name &#39;java -jar server.jar&#39;</code> is pretty much doing exec call:</p> <pre><code>execl(&#34;tmux&#34;, &#34;tmux&#34;, &#34;new&#34;, &#34;-s&#34;, &#34;tmux-session-name&#34;, &#34;java -jar server.jar&#34;) </code></pre> <p>Note how there are no single-quotes in that last argument. They are part of the shell language. The go-equivalent would be</p> <pre><code>exec.Command(&#34;tmux&#34;, &#34;new&#34;, &#34;-s&#34;, &#34;tmux-session-name&#34;, &#34;java -jar server.jar&#34;) </code></pre> <p>Your code boils down to</p> <pre><code>exec.Command(&#34;tmux&#34;, &#34;new&#34;, &#34;-s&#34;, &#34;tmux-session-name&#34;, &#34;&#39;java -jar server.jar&#39;&#34;) </code></pre> <p>(just with more arguments to java), which will try to execute <code>&#39;java</code> instead of <code>java</code>.</p></pre>nathanpaulyoung: <pre><p>Wonderful! Thank you. I&#39;m entirely following now. I expect that all will be well after I implement these changes.</p> <p>I would like to credit you appropriately in the repo. Do you have a github or twitter you would like added to the readme with thanks for your help?</p></pre>TheMerovius: <pre><p>It&#39;s not necessary to give me credit for this :) If you really want to, my github account is Merovius.</p></pre>nathanpaulyoung: <pre><p>Got everything working! Thanks again so much for your help! Now I can move on to more features.</p></pre>nathanpaulyoung: <pre><p>Other reply half-posted and then got edited because mobile.</p> <p>Thank you so much for your help so far. If you want ti see my code, it is on github.</p> <p><a href="http://github.com/nathanpaulyoung/msct" rel="nofollow">http://github.com/nathanpaulyoung/msct</a></p></pre>kron4eg: <pre><p>Check the error. Most likely it will be something like</p> <pre><code>open terminal failed: not a terminal </code></pre></pre>nathanpaulyoung: <pre><p>All the exec.Command.Run() err is giving me is &#34;exit status 1&#34;.</p></pre>kron4eg: <pre><p><a href="https://golang.org/pkg/os/exec/#Cmd.CombinedOutput" rel="nofollow">https://golang.org/pkg/os/exec/#Cmd.CombinedOutput</a> will report better</p></pre>lapingvino: <pre><p>Can you paste your code, I probably have an idea how to fix it.</p></pre>nathanpaulyoung: <pre><p><a href="http://github.com/nathanpaulyoung/msct" rel="nofollow">http://github.com/nathanpaulyoung/msct</a></p></pre>

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

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