You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Go语言中无法将标准输入转发至sudo子进程的问题

问题:Golang执行sudo命令无响应的原因及解决方法

最小可运行示例

package main

import (
    "bufio"
    "fmt"
    "io"
    "log"
    "os"
    "os/exec"
)

func main() {
    //cmd := exec.Command("sudo", "tail", "-10", "/var/log/syslog")
    cmd := exec.Command("cat")
    stdin, err := cmd.StdinPipe()
    if err != nil {
        log.Fatal(err)
    }
    stdout, err := cmd.StdoutPipe()
    if err != nil {
        log.Fatal(err)
    }
    stderr, err := cmd.StderrPipe()
    if err != nil {
        log.Fatal(err)
    }
    if err := cmd.Start(); err != nil {
        log.Fatal(err)
    }
    ForwardStdIn(stdin)
    go ForwardStdOut(stdout)
    go ForwardStdErr(stderr)
    err = cmd.Wait()
    if err == nil {
        os.Exit(0)
    }
    if ee, ok := err.(*exec.ExitError); ok {
        fmt.Println(ee.Error())
        os.Exit(ee.ExitCode())
    } else {
        log.Fatal(err)
    }

}

func ForwardStdOut(stdout io.ReadCloser) {
    scanner := bufio.NewScanner(stdout)
    for scanner.Scan() {
        line := scanner.Text()
        _, err := fmt.Fprint(os.Stdout, line+"\n")
        if err != nil {
            log.Fatal(err)
        }
    }
}

func ForwardStdErr(stderr io.ReadCloser) {
    scanner := bufio.NewScanner(stderr)
    for scanner.Scan() {
        line := scanner.Text()
        _, err := fmt.Fprint(os.Stderr, line+"\n")
        if err != nil {
            log.Fatal(err)
        }
    }
}

func readStdin(ch chan []byte) {
    for {
        buf := make([]byte, 4096)
        n, err := os.Stdin.Read(buf)
        if n == 0 && err == io.EOF {
            close(ch)
            //fmt.Println("stdin closed #1")
            break
        }
        //fmt.Printf("read %v bytes\n", n)
        if n > 0 {
            ch <- buf[:n]
        }
    }
}

func writeStdin(stdin io.WriteCloser, ch chan []byte) {
    for {
        buf, ok := <-ch
        if !ok {
            err := stdin.Close()
            //fmt.Println("stdin closed #2")
            if err != nil {
                log.Fatal(err)
            }
            break
        }
        pos := 0
        for pos < len(buf) {
            n, err := stdin.Write(buf[pos:])
            if err != nil {
                log.Fatal(err)
            }
            //fmt.Printf("wrote %v bytes\n", n)
            pos += n
        }
    }
}

func ForwardStdIn(stdin io.WriteCloser) {
    ch := make(chan []byte, 2)
    go readStdin(ch)
    go writeStdin(stdin, ch)
}

运行现象

  • 当命令为cat时,输入到标准输入的内容会正常输出到标准输出。
  • 替换为sudo tail -10 /var/log/syslog后,程序提示输入密码,输入密码并按下ENTER后无任何反应,既无错误信息也无输出,但调试显示密码已发送至sudo的标准输入。

额外验证

可通过以下命令在无tty的情况下正常执行sudo:

sudo tail -10 /var/log/syslog </dev/null |& cat

疑问

  1. 为何上述代码对cat有效但对sudo无效?
  2. 如何修改代码使其适配sudo或其他使用askpass的程序?

内容的提问来源于stack exchange,提问作者nagylzs

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 08:30:03