douye4051 2019-05-03 02:18
浏览 158
已采纳

go exec对于不同的shell命令的不同行为

I'm trying to use different shell commands for a console go application, and for some reason the behavior is different for the following interactive shells.

This code prints result of a mongoDB query:

cmd := exec.Command("sh", "-c", "mongo --quiet --host=localhost blog")
stdout, _ := cmd.StdoutPipe()

stdin, _ := cmd.StdinPipe()
stdoutScanner := bufio.NewScanner(stdout)

go func() {
    for stdoutScanner.Scan() {
        println(stdoutScanner.Text())
    }
}()

cmd.Start()
io.WriteString(stdin, "db.getCollection('posts').find({status:'ACTIVE'}).itcount()
")

//can't finish command, need to reuse it for other queries
//stdin.Close()
//cmd.Wait()

time.Sleep(2 * time.Second)

But the same code for Neo4J shell does not print anything:

cmd := exec.Command("sh", "-c", "cypher-shell -u neo4j -p 121314 --format plain")
stdout, _ := cmd.StdoutPipe()

stdin, _ := cmd.StdinPipe()
stdoutScanner := bufio.NewScanner(stdout)

go func() {
    for stdoutScanner.Scan() {
        println(stdoutScanner.Text())
    }
}()

cmd.Start()
io.WriteString(stdin, "match (n) return count(n);
")

//can't finish the command, need to reuse it for other queries
//stdin.Close()
//cmd.Wait()
time.Sleep(2 * time.Second)

What is the difference? How can I make the second one work? (without closing the command)

P.S Neo4J works fine when I print directly to os.Stdout:

cmd := exec.Command("sh", "-c", "cypher-shell -u neo4j -p 121314 --format plain")

cmd.Stdout = os.Stdout

stdin, _ := cmd.StdinPipe()

cmd.Start()
io.WriteString(stdin, "match (n) return count(n);
")

//stdin.Close()
//cmd.Wait()
time.Sleep(2 * time.Second)
  • 写回答

1条回答 默认 最新

  • duanpin2009 2019-05-03 20:51
    关注

    When the input to cypher-shell is not an (interactive) terminal, it expects to read the entire input and execute it as a single script. “Entire input” means “everything until EOF”. This is typical for REPL programs: for example, python behaves like this, too.

    So your Cypher code doesn’t even begin executing until you stdin.Close(). Your cmd.Stdout = os.Stdout example appears to work because stdin is implicitly closed when your Go program exits, and only then does cypher-shell execute your code and print to stdout, which is still connected to your terminal.

    You should probably structure your process differently. For example, can’t you run a new cypher-shell for each query?

    However, if all else fails, you can work around this by fooling cypher-shell into thinking that its stdin is a terminal. This is called a “pty”, and you can do it in Go with github.com/kr/pty. The catch is that this also makes cypher-shell print prompts and echo your input, which you will have to detect and discard if you wish to process the output programmatically.

    cmd := exec.Command("sh", "-c", "cypher-shell -u neo4j -p 121314 --format plain")
    f, _ := pty.Start(cmd)
    stdoutScanner := bufio.NewScanner(f)
    cmd.Start()
    
    // Give it some time to start, then read and discard the startup banner.
    time.Sleep(2 * time.Second)
    f.Read(make([]byte, 4096))
    
    go func() {
        for stdoutScanner.Scan() {
            println(stdoutScanner.Text())
        }
    }()
    
    io.WriteString(f, "match (n) return count(n);
    ")
    time.Sleep(2 * time.Second)
    
    io.WriteString(f, "match (n) return count(n) + 123;
    ")
    time.Sleep(2 * time.Second)
    

    Aside 1: In your example, you don’t need sh -c, because you’re not using any features of the shell. You can avoid the overhead of an additional shell process by running cypher-shell directly:

    cmd := exec.Command("cypher-shell", "-u", "neo4j", "-p", "121314", "--format", "plain")
    

    Aside 2: Do not discard returned error values in production code.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 数学的三元一次方程求解
  • ¥20 iqoo11 如何下载安装工程模式
  • ¥15 本题的答案是不是有问题
  • ¥15 关于#r语言#的问题:(svydesign)为什么在一个大的数据集中抽取了一个小数据集
  • ¥15 C++使用Gunplot
  • ¥15 这个电路是如何实现路灯控制器的,原理是什么,怎么求解灯亮起后熄灭的时间如图?
  • ¥15 matlab数字图像处理频率域滤波
  • ¥15 在abaqus做了二维正交切削模型,给刀具添加了超声振动条件后输出切削力为什么比普通切削增大这么多
  • ¥15 ELGamal和paillier计算效率谁快?
  • ¥15 蓝桥杯单片机第十三届第一场,整点继电器吸合,5s后断开出现了问题