Golang中实现交互式bash的方法

Golang中实现交互式bash的方法 团队,

我有一个小的“Hello World” Go 例程,它调用 shell 脚本 hello.sh。 在 hello.sh 中,我编写了一段代码来提示用户输入并简单地打印该输入。 在 hello.go 的 Go 例程中,我需要将控制权传递给 shell 脚本 (hello.sh),以便它能像代码中那样直接要求用户输入。以下是供参考的代码。

Hello.go

package main
import (
"fmt"
//"log"
"os/exec"
"bytes"
//"syscall"
//"bufio"
"os"
//"time"
//"strings"
)

func main() {
fmt.Printf("hello, world\n")
var out bytes.Buffer
var stderr bytes.Buffer
cmd := exec.Command("/bin/sh", "/Users/z003swb/go/nagy/src/hello.sh")
cmd.Stdout = &out
cmd.Stderr = &stderr
cmd.Stdin = os.Stdin
cmd.Run()
fmt.Printf("%s\n", out.String())
}

Hello.sh

echo ""
echo "Calling Hello sh script at `date`"
echo "Hai at `date`"
echo "Do you want to say Yes|No?"
read name
echo "respone was : $name"
echo "Bye at `date`"

我的问题/需要帮助?

+++++++++++++++++++++++++

我希望在执行时显示 shell 中的命令 echo "Do you want to say Yes|No?" 并接收用户的输入。

目前,通过设置 cmd.Stdin = os.Stdin,我能够将值传递给 shell,但我无法直接从 shell 代码中获得调用。

执行示例

$ ./hello
hello, world
yes

Calling Hello sh script at Thu May 6 11:32:07 IST 2021
Hai at Thu May 6 11:32:07 IST 2021
Do you want to say Yes|No?
respone was : yes
Bye at Thu May 6 11:32:10 IST 2021

但是,我如何让 shell 在执行时询问输入并显示“Do you want to say Yes|No?”?有什么方法可以实现这一点吗?请帮助。我是一个初学者,所以需要专业的帮助。谢谢。


更多关于Golang中实现交互式bash的方法的实战教程也可以访问 https://www.itying.com/category-94-b0.html

5 回复

@gucio321

你好,非常感谢你的回复。但是,我该如何让它提示输入“echo ‘Do you want to say Yes|No?’”呢?这个提示在 hello.sh 脚本里。目前这个提示没有出现,这就是问题所在。

我需要通过 Go 代码来调用/提示从 shell 获取输入,并通过 Go 代码将响应返回给 shell。

再次非常感谢你的帖子。

更多关于Golang中实现交互式bash的方法的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


gucio321 提到,如果你想看到 hello.sh 的提示信息并能够通过终端进行响应,那么将命令的 Stdout 设置为 os.Stdout,Stderr 设置为 os.Stderr。这样 hello.sh 就能像在 shell 中直接运行一样与终端进行交互。

naguram: 我需要通过 Go 代码从 shell 调用/提示输入,并通过 Go 将响应返回给 shell。

我不太确定你这里具体想要什么。你是希望用户通过 shell 看到并与 hello.sh 交互,还是希望你的 Go 程序拦截并响应它?

正如其他人所指出的,你可以直接将 Stdin/out/error 分配给 os:

cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr

不过,我敢打赌你没有得到任何提示/输出的原因是 cmd.Run 返回了一个错误,而你却没有处理它。修改这行代码来检查错误:

// 将这一行...
cmd.Run()
// ... 改为这样:
if err := cmd.Run(); err != nil {
	fmt.Println(err)
}

……我敢打赌你会得到一个能确切告诉你问题所在的错误。

编辑:我没意识到我在这里来得这么晚。哎呀。

你不需要使用 bytes.Buffer 作为输出。 你可以直接这样做:cmd.Stdout = os.Stdout; cmd.Stderr = os.Stderr 你也可以编写自己的 io.Writer 实现,例如:

type writer struct {
        w bytes.Buffer
}

func (w *writer) Write(b []byte) (n int, err error) {
        n1, err := os.Stdout.Write(b)
        if err != nil {
                return n1, err
        }

        n2, err := w.w.Write(b)
        if err != nil {
                return n2, err
        }

        return len(b), nil
}

func (w *writer) String() string { return w.w.String() }

*当然,Write 方法应该检查每个 n1 和 n2。

在Golang中实现交互式bash,关键在于正确设置标准输入输出。你的代码已经接近正确,但需要确保shell脚本的提示能实时显示。以下是修改后的示例:

package main

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

func main() {
    fmt.Printf("hello, world\n")
    
    cmd := exec.Command("/bin/sh", "/Users/z003swb/go/nagy/src/hello.sh")
    
    // 关键设置:将标准输入、输出、错误直接连接到终端
    cmd.Stdin = os.Stdin
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr
    
    // 设置终端控制
    cmd.SysProcAttr = &syscall.SysProcAttr{
        Setpgid: true,
        Ctty:    int(os.Stdin.Fd()),
    }
    
    err := cmd.Run()
    if err != nil {
        fmt.Printf("Command execution error: %v\n", err)
    }
}

或者使用更简单的方法,直接传递-i参数启用交互模式:

package main

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

func main() {
    fmt.Printf("hello, world\n")
    
    // 使用-i参数启用交互模式
    cmd := exec.Command("/bin/sh", "-i", "/Users/z003swb/go/nagy/src/hello.sh")
    
    cmd.Stdin = os.Stdin
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr
    
    err := cmd.Run()
    if err != nil {
        fmt.Printf("Error: %v\n", err)
    }
}

如果仍然遇到缓冲问题,可以使用pty创建伪终端:

package main

import (
    "fmt"
    "os"
    "os/exec"
    "github.com/creack/pty"
    "io"
)

func main() {
    fmt.Printf("hello, world\n")
    
    cmd := exec.Command("/bin/sh", "/Users/z003swb/go/nagy/src/hello.sh")
    
    // 创建伪终端
    ptmx, err := pty.Start(cmd)
    if err != nil {
        panic(err)
    }
    defer ptmx.Close()
    
    // 复制标准输入输出
    go func() {
        io.Copy(ptmx, os.Stdin)
    }()
    io.Copy(os.Stdout, ptmx)
    
    cmd.Wait()
}

注意:使用pty方法需要先安装依赖:go get github.com/creack/pty

第一种方法应该能解决你的问题,它会确保shell脚本的提示信息实时显示,并正确接收用户输入。

回到顶部