10

I want to run an interactive shell script in golang program, such as wrap a "ping 8.8.8.8", "python", "bc", "mysql -H -P -u -p". The golang program should exit when itself finish calling an interactive command,or shell, and leaving spawned interactive with user.

I have tried the "exec.Command("python").Run()", but the golang program just finish and leave nothing to me.

func (h ConnectHandler)ConnectMySQL()  {
    logrus.Debug("ConnectMySQL, script:",common.CONF.FilePath.MySQLConnectScriptPath)
    err :=exec.Command("bash",common.CONF.FilePath.MySQLConnectScriptPath).Run()
    if err != nil{
        logrus.Errorf("ConnectMySQL failed, exit 1,%s",err)
        os.Exit(1)
    }
}

2 Answers 2

26

Connect the Command's stdin, stdout, and stderr to those of the parent process. Also, supply -c in exec.Command to bash, else bash will try to run your program as if it's a shell script.

For example launching the interactive Python console:

func main() {
    fmt.Println("Before Python shell:")
    cmd := exec.Command("bash", "-c", "/usr/bin/python3")
    cmd.Stdin = os.Stdin
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr
    _ = cmd.Run() // add error checking
    fmt.Println("After Python shell")
}
Sign up to request clarification or add additional context in comments.

1 Comment

This method works perfectly. I do not understand, why it has not received any upvotes.
7

It sounds like you want to replace the current process with the command you're trying to launch. After you launch the other command, your Go program is gone, and the caller interacts with the launched program as if it were the thing that was initially started.

You need the low-level syscall.Exec function for this. You shouldn't normally expect it to return. Note that you need to provide a number of details like the actual binary to run and the environment that the higher-level wrappers don't need. (A very quick Google search finds this detailed writeup.)

import "os"
import "syscall"
err := syscall.Exec("/bin/ls", []string{"ls", "-l", "/"}, os.Environ())
// We don't expect this to ever return; if it does something is really wrong
os.panic(err)

In terms of the underlying Unix system calls, the higher-level interfaces like os.StartProcess and the exec.Cmd all fork(2) a child process first before execve(2) in that child. When your Go process exits, that child process becomes orphaned, and the system init process becomes its new parent. The shell just sees that the Go process has exited and will produce a new shell prompt.

2 Comments

what's the difference between the answer below using Connect the Command's stdin, stdout, and stderr to those of the parent process?
In my answer there is no "parent process"; the Go program replaces itself with the exec'd program, and the parent and process ID are the same as it was for the Go program. In @ShengjianDing's answer the Go program remains and becomes the parent of the subprocess; it's responsible for checking the error status and other tasks.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.