草庐IT

【Linux】shell命令行简单解释器

平凡的人1 2023-05-20 原文


回顾一下,我们前面学习了进程创建,进程终止,进程等待,进程替换,通过这些内容我们可以来进行实现简单的shell命令行解释器!!!下面我们直接来看一看如何去实现shell命令行解释器:

总体分为(整体需要循环哦):

1.输出提示符

2.输入和获取命令

3.fork创建子进程

4.内建命令


  • 输出提示符

这里的提示字符为用户名@主机名 当前路径# 直接打印出来作为提示所用,也可以自己设置成其他的,问题不大

printf("用户名@主机名 当前路径# ");

同时,这里并没有\n,会有缓冲区的问题,类似于我们之前所说的进度条所遇到的问题,在这个地方哦可以用fflush(stdout)刷新缓冲区

  • 输入和获取命令

输入

我们需要获取一行的内容,利用fgets函数获取,同时,可以定义一个lineCommand[NUM]数组

char*s = fgets(lineCommand,sizeof(lineCommand)-1,stdin);
assert(s != NULL);

但是打印的时候却多换了一行,这是我们把\n也读取到了,直接进行处理即可,清除最后一个\n

lineCommand[strlen(lineCommand)-1] = 0;       

获取

输入之后,我们自然需要去进行获取,我们需要分割命令行,这个地方用strtok。把字符串切割成若干个子串:

strtok:第一次直接传递参数,第二次则必须传NULL。且在最终strtok会返回NULL。

  • fork创建进程

利用fork创建子进程,同时父进程需要等待子进程退出返回结果

另外我们还需要选择替换函数execvp:首先替换函数需要先带上v,可将所有的执行参数放入数组中统一传递,其次还要选择带上p,我们输入的只有程序命令,带上p会自动在环境变量中寻找

至此,基本的框架我们已经搞定了。

  • shell运行原理

同时,在理解一下shell的运行原理:shell内部提取命令行做分析,然后调用exec. shell执行命令必须通过创建子进程,如果不创建子进程会把我们所有的shell全部替换,所以执行命令时一般磁盘上的程序必须创建子进程

  • 内建命令

我们在运行自己写的shell的时候,发现输入cd …输入cd path等命令时发现路径并没有改变!

没有发生改变是因为自己写的shell执行很多命令都要fork()创建子进程,让子进程执行的cd,子进程有自己的工作目录,所以更改的子进程的目录,子进程执行完毕,继续用的是父进程,既shell,并没有影响父进程,所以并没有改变。

对于cd,我们可以采用内建命令:不需要创建子进程执行,让shell自己执行命令,称为内建命令。本质就是执行系统接口,我们可以调用一个系统接口chdir,可解决上述问题:

简易shell——代码实现

#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
#define NUM 1024
#define OPT_NUM 64
char lineCommand[NUM];
char *myargv[OPT_NUM];
int lastCode = 0;
int lastSig = 0;
int main()
{
    while(1)
    {
        //输出提示符
        printf("用户名@主机名 当前路径#");
        fflush(stdout);
        //获取输入
        char*s = fgets(lineCommand,sizeof(lineCommand)-1,stdin);
        assert(s != NULL);
        (void) s;
        lineCommand[strlen(lineCommand)-1] = 0;
        // printf("test:%s\n",lineCommand);
        //ls -a -l -i  字符串切割
        myargv[0] = strtok(lineCommand," ");
        int i = 1;
        if(myargv[0]!= NULL&& strcmp(myargv[0],"ls") == 0)
        {
            myargv[i++] =(char*)"--color=auto";
        }
        //如果没有子串,strtok会返回NULL                                                                   
        while(myargv[i++] = strtok(NULL," "));
        //如果是cd命令, 不需要创建子进程,让shell自己执行对应的命令,本质就是执行系统接口
        //像这种不需要我们的子进程来执行,而是让shell自己执行的命令 --内建 内置命令
        if(myargv[0]!=NULL&& strcmp(myargv[0],"cd")==0)
        {
            if(myargv[1] != NULL) chdir(myargv[1]);
            continue;
        }
        if(myargv[0]!=NULL&& myargv[1]!=NULL && strcmp(myargv[0],"echo")== 0)
        {
            if(strcmp(myargv[1],"$?") == 0)                                                              
            {
                printf("%d %d\n",lastCode,lastSig);
            }
            else 
            {
                printf("%s\n",myargv[1]);
            }
              continue;
        }
        //利用条件编译测试代码是否成功
#ifdef DEBUG 
        for(int i = 0;myargv[i];i++)
        {
            printf("myargv[%d]:%s\n",i,myargv[i]);
        }
#endif
        //执行命令
        pid_t id = fork();
        assert(id!=-1);
        if(id == 0)
        {
            execvp(myargv[0],myargv);
            exit(1);
        }
        int status = 0;
        pid_t ret = waitpid(id,&status,0);
        assert(ret > 0);
        (void)ret;
        lastCode = ((status>>8)&0xFF);
        lastSig = (status & 0X7F);
    }
    return 0;
}

有关【Linux】shell命令行简单解释器的更多相关文章

  1. ruby - 在 Ruby 中编写命令行实用程序 - 2

    我想用ruby​​编写一个小的命令行实用程序并将其作为gem分发。我知道安装后,Guard、Sass和Thor等某些gem可以从命令行自行运行。为了让gem像二进制文件一样可用,我需要在我的gemspec中指定什么。 最佳答案 Gem::Specification.newdo|s|...s.executable='name_of_executable'...endhttp://docs.rubygems.org/read/chapter/20 关于ruby-在Ruby中编写命令行实用程序

  2. ruby-on-rails - 如何在 ruby​​ 交互式 shell 中有多行? - 2

    这可能是个愚蠢的问题。但是,我是一个新手......你怎么能在交互式ruby​​shell中有多行代码?好像你只能有一条长线。按回车键运行代码。无论如何我可以在不运行代码的情况下跳到下一行吗?再次抱歉,如果这是一个愚蠢的问题。谢谢。 最佳答案 这是一个例子:2.1.2:053>a=1=>12.1.2:054>b=2=>22.1.2:055>a+b=>32.1.2:056>ifa>b#Thecode‘if..."startsthedefinitionoftheconditionalstatement.2.1.2:057?>puts"f

  3. ruby - 简单获取法拉第超时 - 2

    有没有办法在这个简单的get方法中添加超时选项?我正在使用法拉第3.3。Faraday.get(url)四处寻找,我只能先发起连接后应用超时选项,然后应用超时选项。或者有什么简单的方法?这就是我现在正在做的:conn=Faraday.newresponse=conn.getdo|req|req.urlurlreq.options.timeout=2#2secondsend 最佳答案 试试这个:conn=Faraday.newdo|conn|conn.options.timeout=20endresponse=conn.get(url

  4. ruby - 有人可以帮助解释类创建的 post_initialize 回调吗 (Sandi Metz) - 2

    我正在阅读SandiMetz的POODR,并且遇到了一个我不太了解的编码原则。这是代码:classBicycleattr_reader:size,:chain,:tire_sizedefinitialize(args={})@size=args[:size]||1@chain=args[:chain]||2@tire_size=args[:tire_size]||3post_initialize(args)endendclassMountainBike此代码将为其各自的属性输出1,2,3,4,5。我不明白的是查找方法。当一辆山地自行车被实例化时,因为它没有自己的initialize方法

  5. ruby - 用 Ruby 编写一个简单的网络服务器 - 2

    我想在Ruby中创建一个用于开发目的的极其简单的Web服务器(不,不想使用现成的解决方案)。代码如下:#!/usr/bin/rubyrequire'socket'server=TCPServer.new('127.0.0.1',8080)whileconnection=server.acceptheaders=[]length=0whileline=connection.getsheaders想法是从命令行运行这个脚本,提供另一个脚本,它将在其标准输入上获取请求,并在其标准输出上返回完整的响应。到目前为止一切顺利,但事实证明这真的很脆弱,因为它在第二个请求上中断并出现错误:/usr/b

  6. ruby-on-rails - 简单的 Ruby on Rails 问题——如何将评论附加到用户和文章? - 2

    我意识到这可能是一个非常基本的问题,但我现在已经花了几天时间回过头来解决这个问题,但出于某种原因,Google就是没有帮助我。(我认为部分问题在于我是一个初学者,我不知道该问什么......)我也看过O'Reilly的RubyCookbook和RailsAPI,但我仍然停留在这个问题上.我找到了一些关于多态关系的信息,但它似乎不是我需要的(尽管如果我错了请告诉我)。我正在尝试调整MichaelHartl'stutorial创建一个包含用户、文章和评论的博客应用程序(不使用脚手架)。我希望评论既属于用户又属于文章。我的主要问题是:我不知道如何将当前文章的ID放入评论Controller。

  7. ruby - 使用 Ruby 通过 Outlook 发送消息的最简单方法是什么? - 2

    我的工作要求我为某些测试自动生成电子邮件。我一直在四处寻找,但未能找到可以快速实现的合理解决方案。它需要在outlook而不是其他邮件服务器中,因为我们有一些奇怪的身份验证规则,我们需要保存草稿而不是仅仅发送邮件的选项。显然win32ole可以做到这一点,但我找不到任何相当简单的例子。 最佳答案 假设存储了Outlook凭据并且您设置为自动登录到Outlook,WIN32OLE可以很好地完成此操作:require'win32ole'outlook=WIN32OLE.new('Outlook.Application')message=

  8. postman——集合——执行集合——测试脚本——pm对象简单示例02 - 2

    //1.验证返回状态码是否是200pm.test("Statuscodeis200",function(){pm.response.to.have.status(200);});//2.验证返回body内是否含有某个值pm.test("Bodymatchesstring",function(){pm.expect(pm.response.text()).to.include("string_you_want_to_search");});//3.验证某个返回值是否是100pm.test("Yourtestname",function(){varjsonData=pm.response.json

  9. ruby-on-rails - rbenv:从 RVM 移动到 rbenv 后,在 Jenkins 执行 shell 中找不到命令 - 2

    我从Ubuntu服务器上的RVM转移到rbenv。当我使用RVM时,使用bundle没有问题。转移到rbenv后,我在Jenkins的执行shell中收到“找不到命令”错误。我内爆并删除了RVM,并从~/.bashrc'中删除了所有与RVM相关的行。使用后我仍然收到此错误:rvmimploderm~/.rvm-rfrm~/.rvmrcgeminstallbundlerecho'exportPATH="$HOME/.rbenv/bin:$PATH"'>>~/.bashrcecho'eval"$(rbenvinit-)"'>>~/.bashrc.~/.bashrcrbenvversions

  10. Qt Designer的简单使用 - 2

    在前面两节的例子中,主界面窗口的尺寸和标签控件显示的矩形区域等,都是用C++代码编写的。窗口和控件的尺寸都是预估的,控件如果多起来,那就不好估计每个控件合适的位置和大小了。用C++代码编写图形界面的问题就是不直观,因此Qt项目开发了专门的可视化图形界面编辑器——QtDesigner(Qt设计师)。通过QtDesigner就可以很方便地创建图形界面文件*.ui,然后将ui文件应用到源代码里面,做到“所见即所得”,大大方便了图形界面的设计。本节就演示一下QtDesigner的简单使用,学习拖拽控件和设置控件属性,并将ui文件应用到Qt程序代码里。使用QtDesigner设计界面在开始菜单中找到「Q

随机推荐