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

在C语言中实现基础Shell:嵌套命令列表的数据结构选型

用C实现基础Shell:命令列表的最佳表示方案

我正在用C语言实现一个基础Shell,卡在了命令列表的最佳表示方式上——每个条目可能包含多个并行执行的命令。比如解析以下批处理文件:

ls -l directory
cat README.md & du -sh directory
exit

会生成三层嵌套的逻辑结构:

[
  [
    ["ls", "-l", "directory"],
  ],
  [
    ["cat", "README.md"],
    ["du", "-sh", "directory"],
  ],
  [
    ["exit"],
  ],
]

我现在纠结的是exec_many函数的参数数据结构选择:

int exec_many(/* list of list of list of strings */) {
    ...
}

我愿意实现vector的子集,但希望采用更符合C语言风格的方案。

已尝试方案

我想到的方案是定义多层结构体:

struct Command {
    char **args;
    int argc;
};

struct Commands {
    struct Command *commands;
    int count;
};

int exec_many(struct Commands *commands, int count) {
    ...
}

但这个方案有两个问题:

  • 为每个嵌套层定义结构体显得繁琐冗余
  • 构建结构需要预先知晓长度

优化方案

语义化结构体+动态扩展

C语言中,语义清晰的结构体其实是比多级指针更易维护的选择,我们可以通过typedef简化命名,再配合动态扩展的辅助函数解决预先知长的问题:

#include <stdlib.h>
#include <string.h>

// 单个命令的参数集合:argc为参数数量,args以NULL结尾(适配execvp)
typedef struct {
    char** args;
    int argc;
} CmdArgs;

// 一组并行执行的命令:count为并行命令数,cmds是命令数组
typedef struct {
    CmdArgs* cmds;
    int count;
} ParallelCmds;

// exec_many接收串行执行的命令组列表,group_count是串行组的数量
int exec_many(ParallelCmds* serial_groups, int group_count) {
    // 实现逻辑示例:
    // 1. 遍历每个串行组
    // 2. 对组内每个命令fork子进程并exec执行
    // 3. 等待当前组所有子进程结束后,再处理下一个串行组
    return 0;
}

动态扩展辅助函数

实现类似vector的动态添加功能,这样构建结构时无需预先知道长度:

// 初始化空的CmdArgs
CmdArgs cmd_args_init() {
    CmdArgs args = {NULL, 0};
    return args;
}

// 向CmdArgs添加参数,成功返回0,失败返回-1
int cmd_args_add(CmdArgs* args, const char* arg) {
    // 预留NULL结尾的位置,所以长度+2
    char** new_args = realloc(args->args, sizeof(char*) * (args->argc + 2));
    if (!new_args) return -1;
    
    args->args = new_args;
    args->args[args->argc] = strdup(arg);
    if (!args->args[args->argc]) return -1;
    
    args->argc++;
    args->args[args->argc] = NULL; // 适配execvp的参数要求
    return 0;
}

// 初始化空的ParallelCmds
ParallelCmds parallel_cmds_init() {
    ParallelCmds cmds = {NULL, 0};
    return cmds;
}

// 向ParallelCmds添加一个CmdArgs,成功返回0,失败返回-1
int parallel_cmds_add(ParallelCmds* p_cmds, CmdArgs cmd) {
    CmdArgs* new_cmds = realloc(p_cmds->cmds, sizeof(CmdArgs) * (p_cmds->count + 1));
    if (!new_cmds) return -1;
    
    p_cmds->cmds = new_cmds;
    p_cmds->cmds[p_cmds->count] = cmd;
    p_cmds->count++;
    return 0;
}

解析时的使用示例

解析批处理文件时,可以这样动态构建结构:

// 示例:构建第二个串行组(cat & du)
ParallelCmds serial_group = parallel_cmds_init();

CmdArgs cat_cmd = cmd_args_init();
cmd_args_add(&cat_cmd, "cat");
cmd_args_add(&cat_cmd, "README.md");
parallel_cmds_add(&serial_group, cat_cmd);

CmdArgs du_cmd = cmd_args_init();
cmd_args_add(&du_cmd, "du");
cmd_args_add(&du_cmd, "-sh");
cmd_args_add(&du_cmd, "directory");
parallel_cmds_add(&serial_group, du_cmd);

// 将该串行组加入到全局的串行组列表中

方案优势

  1. 语义清晰:每个结构体对应明确的执行单元(单个命令参数、并行命令组、串行命令序列),代码可读性和维护性远高于char****这类多级指针。
  2. 动态构建:通过realloc和辅助函数,解析时可以逐步添加内容,无需预先知晓所有命令的长度。
  3. 适配系统调用:CmdArgs的args以NULL结尾,可以直接传给execvp等系统调用,无需额外处理。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 07:59:53