【问题标题】:Writing a Basic Shell编写基本外壳
【发布时间】:2011-06-14 20:58:35
【问题描述】:

对于我的班级,我必须创建一个类似于 bash 的基本 shell,它允许用户调用 ls、sleep 等命令。 我正在寻找有关如何执行此操作的资源:教程、帮助文本、示例代码,甚至只是有关如何开始的一些一般信息。 有人有链接或信息可以帮助我吗?

【问题讨论】:

  • 你需要它来解析 shell 脚本(或者像for x in `seq 0 10`; do something; done这样的任意命令行调用吗?
  • 没有shell脚本,只是执行linux内置命令和做一些文件结构操作
  • 在高层次上,想法是读取输入字符串并执行 fork/exec。对于管道进程,将 fork/exec 习惯用法与 dup2 组合并关闭以连接管道。

标签: c bash shell


【解决方案1】:

这真的取决于你的外壳必须有多简单。如果您不需要作业控制(即后台处理)或管道,那么它非常简单。这是一个例子:

#include <stdio.h>
#include <stdlib.h>

#define MAX_LENGTH 1024

int main(int argc, char *argv[]) {
  char line[MAX_LENGTH];

  while (1) {
    printf("$ ");
    if (!fgets(line, MAX_LENGTH, stdin)) break;
    system(line);
  }

  return 0;
}

您可以使用 CTRL-D 退出上述示例。要添加像 exitcd 这样的内置命令,您必须使用 strtok() 标记该行并查看第一个标记。这是一个添加了这些命令的更复杂的示例:

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

#ifdef _WIN32
#include <windows.h>
#define chdir _chdir

#else
#include <unistd.h>
#endif

#define MAX_LENGTH 1024
#define DELIMS " \t\r\n"

int main(int argc, char *argv[]) {
  char *cmd;
  char line[MAX_LENGTH];

  while (1) {
    printf("$ ");
    if (!fgets(line, MAX_LENGTH, stdin)) break;

    // Parse and execute command
    if ((cmd = strtok(line, DELIMS))) {
      // Clear errors
      errno = 0;

      if (strcmp(cmd, "cd") == 0) {
        char *arg = strtok(0, DELIMS);

        if (!arg) fprintf(stderr, "cd missing argument.\n");
        else chdir(arg);

      } else if (strcmp(cmd, "exit") == 0) {
        break;

      } else system(line);

      if (errno) perror("Command failed");
    }
  }

  return 0;
}

您可以通过添加更多内置命令或支持诸如 cd 之类的内容来扩展此功能,无需参数即可更改为您的主目录。您还可以通过添加当前目录等信息来改进命令提示符。

附带说明一下,添加命令历史记录和行编辑功能的简单方法是使用 GNU readline 库。

【讨论】:

  • @AliShakiba strtok 保留上下文。传递零会导致它继续解析它停止的地方。您应该阅读 strtok 手册页。
【解决方案2】:

我为HelenOS project 编写了一个非常基本的shell。它具有以下特点:

  • 行编辑/历史记录
  • 内置命令/可加载命令
  • 查找外部命令的搜索路径
  • 极其基础的脚本
  • 将大多数 GNU 核心实用程序(从头开始)的实现简化为内置函数。

其中大部分是在一个非常简单的可重用框架上实现的,该框架围绕函数指针构建。至少,您需要一个内置的“exit”或“quit”,以便有人可以真正脱离外壳,加上“cd”和“pwd”。内置的“导出”/“声明”也是有意义的。

您可以查看代码 (BSD) 许可 here。或者下载 repo,它在 uspace/app/bdsh 中。如果您需要,我可能会在移植它之前挖掘最后一个可用的 Linux 版本。最大的不同是 HelenOS 版本使用了一个自制的行编辑器,task_spawn() 而不是 execve() / posix_spawn() 等。其余的都是可移植的。它最初的设计只是为了使功能测试变得简单和交互。我没有实施工作控制,因为它不是必需的。然而,这可能是微不足道的。

如果你想研究一个“真正的”shell,我强烈建议你查看dash,你会发现它比直接研究 bash 的代码更容易掌握。

有趣的是,“bdsh”代表“brain dead shell”。

【讨论】:

    【解决方案3】:

    您可以查看 BusyBox 以了解一些非常小的 shell 实现。

    【讨论】:

      【解决方案4】:

      如果您刚开始,请尝试http://stephen-brennan.com/2015/01/16/write-a-shell-in-c/ 并进一步了解详细知识http://www.gnu.org/software/libc/manual/html_node/Data-Structures.html#Data-Structures

      此外,在学习如何使用 C/C++ 编写 shell 时,请养成参考命令手册页的习惯。

      【讨论】:

        【解决方案5】:

        Bash 仅提供很少的命令:cdpushdpopd、变量处理例程(set$x)以及所有控制流项,如循环和条件。几乎所有其他命令都可以在$PATH 中找到。

        除非赋值另有说明,否则忽略变量和控制流。只需提供一个REPL,它在$PATH 中查找并执行用户输入的任何内容(可能是通过posix_spawn())并传递命令行参数。

        【讨论】:

          【解决方案6】:

          低调的回答:

          如果程序很简单,那么简单的系统调用可以通过命令“system("putdesiredcommandhere");”实现类似脚本的方式

          当您在程序中安装“printf”时,您是在 stdio.h 的帮助下进行一个简单的系统调用。

          下面是小代码示例。

          #include <stdio.h>
          #include <stdlib.h>
          main ()
          {
              system ("clear");
              system ("echo this is an example of a basic system call");
              printf ("just as you've always done.\n");
          
              return 0;
          }
          

          使用该方法,您可以将 bash 或 csh 脚本转录为 c 程序。它需要对脚本中的每一行使用系统调用。

          【讨论】:

            猜你喜欢
            • 2023-04-08
            • 2021-09-13
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-01-31
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多