首页 > 解决方案 > Go 如何编写代码并执行系统命令以启动新终端?

问题描述

当我在Go代码中执行一个普通的命令时,例如ls -ls,它可以正常执行并返回结果:

// Error handling is omitted, in fact there is error handling in my code

func main() {
    for {
        var stdout bytes.Buffer
        cmd := exec.Command("sh","-c","ls -ls")
        cmd.Stdout = &stdout
        cmd.Run()
        fmt.Println(stdout.String())
    }
}

执行结果如下:</p>

[root@10-x-x-xx /root]# go run main.go
/usr/bin/sh -c ls -ls
stdout: total 8
4 -rw-r--r-- 1 root root 358 Aug 14 23:09 main.go
4 -rw-r--r-- 1 root root 167 Aug 14 16:07 temp.yaml

但是当我执行一个命令启动一个新终端时(不确定我的理解是否正确),程序正常返回,没有错误,没有输出,也没有跳转到新终端。

例如:nsenter -n --target PID。这是进入网络命名空间的命令。正常情况下,它会返回一个“#”并输入成功。

// Error handling is omitted, in fact there is error handling in my code

func main() {
    for {
        var stdout bytes.Buffer
        cmd := exec.Command("sh","-c","nsenter -n --target 123")
        cmd.Stdout = &stdout
        cmd.Run()
        fmt.Println(stdout.String())
    }
}

程序执行完成后退出,但并没有真正进入网络命名空间。

但是当我使用python时,没有这个问题,它是可执行的。

import os
os.system("nsenter -n --target 24347")

或者另一个问题,如何在Go中编写代码,以便我可以执行这段代码来ssh到其他主机,我觉得这似乎是同样的问题。

标签: go

解决方案


你有几个问题。

首先,您尝试生成一个交互式命令(nsenter默认情况下将在目标命名空间中启动一个 shell),但您还将stdout连接到一个变量,这意味着即使它有效,您也看不到任何输出直到命令退出,这可能不是你想要的。

其次,您没有将标准输入连接任何有用的东西,这意味着大多数交互式命令将简单地退出。

要在 Go 中启动交互式子命令,您可以编写如下内容:

package main

import (
    "os"
    "os/exec"
)

func main() {
    cmd := exec.Command("bash", "--norc")
    cmd.Stdout = os.Stdout
    cmd.Stdin = os.Stdin
    cmd.Stderr = os.Stderr
    cmd.Env = os.Environ()
    cmd.Env = append(cmd.Env, "PS1=example$ ")
    cmd.Run()
}

(这bash会使用提示符启动一个 shell example$

这里的关键是我们将stdinstdoutstderr连接 到父进程的相应描述符,这允许我们与子进程交互。

如果您不尝试启动交互式外壳,那么您现在拥有的就可以了。也就是说,你可以写:

package main

import (
    "bytes"
    "fmt"
    "os/exec"
)

func main() {
    var stdout bytes.Buffer
    cmd := exec.Command("sudo", "nsenter", "-n", "--target", "2399896", "ip", "addr")
    cmd.Stdout = &stdout
    cmd.Run()
    fmt.Println(stdout.String())
}

以上工作正常,并显示在ip addr目标网络命名空间内运行的结果。

请注意,您可能更通常使用模块中的Output方法 exec

package main

import (
    "fmt"
    "os/exec"
)

func main() {
    cmd := exec.Command("sudo", "nsenter", "-n", "--target", "2399896", "ip", "addr")
    stdout, err := cmd.Output()
    if err != nil {
        panic(err)
    }
    fmt.Println(string(stdout))
}

推荐阅读