当用户在沙箱容器内执行安装程序(如安装 Go、Node.js)时,安装脚本会修改 ~/.bashrc、/etc/profile 等配置文件,但这些环境变量在后续命令中不生效。
当前方案:使用
bash -l -c执行命令- 问题:假设容器内有 bash,但某些镜像(如 Alpine)只有
sh
- 问题:假设容器内有 bash,但某些镜像(如 Alpine)只有
错误方案:使用
sh -c ". ~/.bashrc"- 问题:
~/.bashrc可能包含source等 bash 专用语法,sh无法解析
- 问题:
环境变量获取错误:使用
$SHELL- 问题:
$SHELL是宿主机的环境变量,不是容器内的
- 问题:
从容器内的 /etc/passwd 获取用户的默认 shell,然后用该 shell 的 login mode 执行命令。
# 示例命令
docker exec -it <container> $(docker exec <container> grep "^root:" /etc/passwd | cut -d: -f7)
type dockerContainer struct {
name string
started bool
shell string // 新增:缓存的用户默认 shell
}
// 在容器创建后,获取用户的默认 shell
func (s *dockerSandbox) detectShell(containerName string) string {
cmd := exec.Command("docker", "exec", containerName,
"sh", "-c", "grep '^root:' /etc/passwd | cut -d: -f7")
output, err := cmd.Output()
if err != nil || len(strings.TrimSpace(string(output))) == 0 {
return "/bin/sh" // fallback
}
return strings.TrimSpace(string(output))
}
func (s *dockerSandbox) Wrap(...) (string, []string, error) {
// ... 获取容器 ...
// 获取缓存的 shell
shell := "/bin/sh"
if c, ok := s.containers[userID]; ok && c.shell != "" {
shell = c.shell
}
dockerArgs := []string{
"exec",
"-i",
"-w", "/workspace",
containerName,
shell, "-l", "-c", // login shell,自动加载所有配置
command,
}
// ...
}
由于使用 login shell,命令格式需要调整:
// 之前
dockerArgs = append(dockerArgs, containerName, command)
dockerArgs = append(dockerArgs, args...)
// 之后(使用 shell -l -c)
shellCmd := command
if len(args) > 0 {
shellCmd = command + " " + strings.Join(args, " ")
}
dockerArgs = append(dockerArgs, containerName, shell, "-l", "-c", shellCmd)
| Shell | Login Mode 行为 |
|---|---|
| bash | -l 加载 /etc/profile, ~/.bash_profile, ~/.bashrc |
| zsh | -l 加载 /etc/zprofile, ~/.zprofile, ~/.zshrc |
| sh | -l 大多忽略,需要手动 source(但 POSIX shell 通常不依赖 rc 文件) |
| ash/dash | -l 行为类似 sh |
export VAR=value 持久化到 ~/.xbot_env 的逻辑保持不变:
- 解析命令中的
export语句 - 写入
~/.xbot_env - 确保
~/.bashrc或~/.zshrcsource~/.xbot_env
- 容器无 shell 字段:fallback 到
/bin/sh - login shell 不支持:某些最小化镜像可能不支持
-l,可尝试不用-l - 配置文件语法错误:用户配置文件可能有错误,需要静默忽略
- 修改
dockerContainer结构体,添加shell字段 - 在
getOrCreateContainer中,容器启动后检测 shell - 修改
Wrap方法,使用缓存的 shell 执行命令 - 修改
shell.go,移除硬编码的bash -l,改用沙箱提供的 shell - 更新测试用例
# Alpine 镜像(只有 sh)
docker run --rm alpine sh -c "grep '^root:' /etc/passwd | cut -d: -f7"
# 输出: /bin/sh
# Ubuntu 镜像
docker run --rm ubuntu grep '^root:' /etc/passwd | cut -d: -f7
# 输出: /bin/bash
# 安装后环境变量生效测试
# 1. 执行安装脚本(修改 ~/.bashrc)
# 2. 后续命令中环境变量应该生效