编写基本外壳

Posted

技术标签:

【中文标题】编写基本外壳【英文标题】: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 组合并关闭以连接管道。 【参考方案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 程序。它需要对脚本中的每一行使用系统调用。

【讨论】:

以上是关于编写基本外壳的主要内容,如果未能解决你的问题,请参考以下文章

shell简介及基本使用

Linux学习之基本概念

如何在shell脚本中,判断一个基本命令执行是不是成功

基于应用外壳的架构

更改外壳文本颜色(Windows)[重复]

如何创建项目外壳