用C语言构建简单Shell:系统编程初学者指南(2-3)

我如何用C语言构建简单Shell——系统编程初学者指南 (2/3)
#cli #c #systemprogramming #memorymanagement
第2部分:在自定义Shell中解析用户输入(C编程)
在我的自定义shell项目的这一部分中,我将解释如何在C中动态解析用户输入。在shell环境中正确解析输入是至关重要的,因为命令通常包含多个参数。我没有依赖固定大小的缓冲区,而是实现了动态内存分配方法以获得更好的灵活性。
理解 _parser_ 函数
parser() 函数负责:
动态将用户命令分割成参数
高效管理内存分配和重新分配
返回参数数组以供进一步处理
代码分解
  1. #ifndef PARSER_H
  2. #define PARSER_H

  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. #include <string.h>

  6. #define arg_buffer_size 4

  7. char **parser(char *command, int *argc);
  8. void free_args(char **args, int argc);

  9. #endif
头文件保护 (#ifndef PARSER_H): 防止多次包含。
常量定义 (arg_buffer_size): 设置参数存储的初始缓冲区大小。
函数原型: 声明用于分割输入的 parser() 函数和用于内存清理的 free_args()
_parser()_ 的实现
  1. char **parser(char *command, int *argc)
  2. {
  3. int size = arg_buffer_size;
  4. int length = 0;
  5. char **args = malloc(size * sizeof(char *));
内存分配 (malloc): 为字符串数组分配内存。
错误处理: 虽然没有明确显示,但应该检查 malloc 以防止失败。
  1. *argc = 0;
  2. char *token = strtok(command, " ");
  3. while (token)
  4. {
  5. if (*argc >= size)
  6. {
  7. size *= 2;
  8. args = realloc(args, size * sizeof(char *));
  9. }
  10. args[*argc] = strdup(token);
  11. (*argc)++;
  12. token = strtok(NULL, " ");
  13. }
  14. args[*argc] = NULL;
  15. return args;
  16. }
标记化输入
使用 strtok() 将输入分割成单独的参数。
遍历每个标记并动态存储它。
动态扩展内存 (realloc)
如果参数计数超过分配的大小,缓冲区会加倍。
realloc() 尝试安全地调整缓冲区大小。
存储参数
使用 strdup() 复制每个标记以避免内存损坏。
最后一个参数设置为 NULL 以正确执行命令。
清理内存
  1. void free_args(char **args, int argc)
  2. {
  3. for (int i = 0; i < argc; i++)
  4. {
  5. free(args[i]);
  6. }
  7. free(args);
  8. }
防止内存泄漏: 释放每个参数和整个数组以确保高效的内存管理。
将解析器集成到Shell中
parser() 函数集成到shell的主循环中,允许命令被正确处理。
  1. int main(void)
  2. {
  3. Command commands[] = {
  4. {"help", help_action},
  5. {"clr", clear_action},
  6. {"say", say_action},
  7. {"exit", exit_action},
  8. {"cwp", cwp_command},
  9. {"date", date_command},
  10. {"clone", clone_file_command},
  11. {"cut", cut_file_command},
  12. {"read", read_file_command},
  13. {"del", delete_file_command},
  14. {"open", open_program_command},
  15. {"list", list_of_directory}};

  16. int command_count = sizeof(commands) / sizeof(commands[0]);
  17. while (1)
  18. {
  19. int argc;
  20. printf("shell_of_mine => ");
  21. char *command = read_command();
  22. char **args = parser(command, &argc);
  23. execute_command(args, argc, commands, command_count);
  24. free_args(args, argc);
  25. free(command);
  26. }
  27. return 0;
  28. }
主循环: 持续读取、解析和执行用户命令。
动态解析: 解析器动态提取参数,允许灵活的命令处理。
内存清理: free_args() 确保执行后释放分配的内存。
为什么采用这种方法?
📌 处理任意命令长度: 与静态数组不同,动态分配确保灵活性。
📌 高效的内存管理: 使用 realloc() 动态优化内存使用。
📌 防止缓冲区溢出: 消除可能导致错误的固定大小限制。