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

Go中嵌套Shell执行异常排查:为何bash启动后进程未切换

问题排查:Go执行shell命令序列未切换进程

问题描述

编写Go函数模拟终端执行命令序列:ps -p $$、bash、ps -p $$,预期第一次输出当前shell为sh,启动bash后第二次输出bash,但实际两次均输出sh。

代码与输出

代码

func main() {
    var commands_list []string = []string{
        "ps -p $$",
        "bash",
        "ps -p $$",
    }
    output, err := ExecCmdSHELL(commands_list)
    if err != nil {
        fmt.Println(err)
        fmt.Println("-----------")
        fmt.Println(output.Exit_code)
    } else {
        fmt.Println(err)
        fmt.Println("-----------")
        fmt.Println(output.Exit_code)
        fmt.Println("-----------")
        fmt.Println(output.Stdout_str)
        fmt.Println("-----------")
        fmt.Println(output.Stderr_str)
    }
}

func ExecCmdMainSHELL(commands_list[] string, windows_shell string, linux_shell string) (CmdOutput, error) {
    if windows_shell == "" {
        windows_shell = "powershell.exe"
    }
    if linux_shell == "" {
        linux_shell = "sh"
    }
    var shell string = ""
    if "windows" == runtime.GOOS {
        shell = windows_shell
    } else {
        shell = linux_shell
    }

    var commands_str string = ""
    for _, command := range commands_list {
        commands_str += command
        if command != "" && !strings.HasSuffix(command, "\n") {
            fmt.Println("Adding newline to command " + command)
            commands_str += "\n"
        }
    }

    fmt.Println(commands_str)

    var stdout bytes.Buffer
    var stderr bytes.Buffer

    cmd := exec.Command(shell)
    cmd.Stdout = &stdout
    cmd.Stderr = &stderr
    cmd.Stdin = strings.NewReader(commands_str)
    err := cmd.Run()

    var stdout_str = strings.ReplaceAll(stdout.String(), "\r\n", "\n")
    stdout_str = strings.ReplaceAll(stdout_str, "\r", "\n")
    var stderr_str = strings.ReplaceAll(stderr.String(), "\r\n", "\n")
    stderr_str = strings.ReplaceAll(stderr_str, "\r", "\n")

    var exit_code int = 0
    if err != nil {
        var exiterr *exec.ExitError
        if errors.As(err, &exiterr) {
            exit_code = exiterr.ExitCode()
            err = nil
        } else {
            exit_code = GENERIC_ERR
        }
    }

    return CmdOutput{
        Stdout_str: stdout_str,
        Stdout:     &stdout,
        Stderr_str: stderr_str,
        Stderr:     &stderr,
        Exit_code:  exit_code,
    }, err
}

输出

PID TTY          TIME CMD
 955719 pts/0    00:00:00 sh
    PID TTY          TIME CMD
 955719 pts/0    00:00:00 sh

问题根源

  1. Shell变量提前替换:$$是Shell的特殊变量,表示当前进程PID。当所有命令一次性传给sh的标准输入时,sh会在解析整个命令串阶段就将所有$$替换为自身的PID,因此第三个命令ps -p $$实际执行的是ps -p [sh的PID],无论后续启动bash进程,查询的始终是sh的进程。
  2. 命令执行流程问题:即使bash启动后读取剩余输入命令,由于$$已被sh提前替换,执行结果还是sh的进程信息。另外,默认情况下bash启动后进入交互模式,若无额外输入会直接退出,后续命令仍由原sh进程执行。

解决方案

方案1:转义$$避免提前替换

将第三个命令中的$$转义,让sh不进行变量替换,传递给bash后由bash解析:

var commands_list []string = []string{
    "ps -p $$",
    "bash",
    "ps -p \\$\\$",
}

方案2:让bash直接执行目标命令

修改命令序列,让bash通过-c参数直接执行ps -p $$,确保命令在bash上下文执行:

var commands_list []string = []string{
    "ps -p $$",
    "bash -c 'ps -p $$'",
}

方案3:分阶段执行命令

若需模拟交互式Shell的连续执行,可分两次启动Shell:第一次执行ps -p $$,第二次启动bash并执行ps -p $$,确保每个阶段的命令在对应Shell进程中执行。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 02:34:55