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
问题根源
- Shell变量提前替换:
$$是Shell的特殊变量,表示当前进程PID。当所有命令一次性传给sh的标准输入时,sh会在解析整个命令串阶段就将所有$$替换为自身的PID,因此第三个命令ps -p $$实际执行的是ps -p [sh的PID],无论后续启动bash进程,查询的始终是sh的进程。 - 命令执行流程问题:即使
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
相关产品推荐
相关产品推荐

