精华内容
下载资源
问答
  • 用C语言在Linux系统上模拟命令行执行指令,该文件需要在Linux系统上运行,Windows会有异常
  • 用c语言实现简单的命令解析器 实现eoc设备命令,和linux自身命令
  • Shell项目:命令解析器

    千次阅读 2018-09-16 13:05:18
    命令解析器:  提示信息:运行自己的Shell时,首先会像系统shell一样,输出一段提示信息, 从左到右依次是:登陆时的用户名,主机信息,和当前目录。  我们定义一个函数Put Flag()来实现, 这里涉及到几个...

    命令解析器:

           提示信息:运行自己的Shell时,首先会像系统shell一样,输出一段提示信息,

    从左到右依次是:登陆时的用户名,主机信息,和当前目录。

           我们定义一个函数Put Flag()来实现,

    这里涉及到几个大家可能不太熟悉的函数和结构体

      

    切割命令(识别命令):

    输出了提示信息后,用户就要输入他想执行的命令了,那我们如何来识别用户输入的命令呢,又是怎样识别用户输入的是参数还是命令呢,这里我们实现一个CutCmd函数,顾名思义就是切割命令。

    这里strtok函数就是用来切割命令的函数,也是该函数的核心。第一次调用时必须将要切割的字符串cmd传进去,以空格为切割标记,细心的同学可能会发现在下面while循环中strtok函数的参数传了一个NULL,这和strtok函数的实现机制有关。strtok()在参数cmd的字符串中发现第二个参数中包含的分割字符时,则会将该字符改为\0 字符。在第一次调用时,strtok()必需给予参数cmd字符串,往后的调用则将参数cmd设置成NULL。每次调用成功则返回指向被分割出片段的指针cmd为空值NULL,则函数保存的指针SAVE_PTR在下一次调用中将作为起始位置。

    到这里我们的准备工作差不多已经完善了,接下来就到了根据用户输入的命令去执行相关可执行文件的时候了

    进程替换和子进程:

           当用户输入命令后,我们要根据用户输入的命令去选择相应的可执行文件来实现该命令,这里会用到一个exec函数,它实现了一个进程替换的功能,就是把当前进程的代码从调用该函数处替换成另外一个文件,而进程号不变,在系统的终端程序中,一个命令执行完之后又会回到命令解析器中,也就是说命令解析器的主程序应该是一个死循环,但是如果调用exec函数替换进程之后,这个死循环就会退出,也就是说我们这个终端每次执行一个命令就要重新启动一次,显然与系统终端有很大差别,为了消除这一差异,我们在这里使用fork函数产生一个子进程,然后把进程替换实现在子进程中,这样,每执行完一个命令,子进程结束,又会回到父进程的命令解析器的死循环中。

    这里用红色下划线标注的是两个不能在子进程中实现的命令,一个是exit,退出终端的命令,执行此命令后,终端程序结束,终端退出。还有一个是cd命令,切换当前目录,如果在子进程中实现,当前目录切换之后子进程结束,又会回到当前目录,因此cd命令应该实现在父进程中。我们封装了一个函数来实现cd命令

    命令解析器基本就是这些内容,接下来就是各个命令的具体实现了。

    展开全文
  • linux下shell命令解析器

    千次阅读 2018-11-05 16:35:33
    shell是一个命令语言解析器,有自己内建的shell命令集,shell也能被系统中其他应用程序所调用。用户在提示符下输入的命令都由shell先解释然后传给linux核心。 当普通用户成功登陆,系统将执行一个称为shell的程序,...

    shell是一个命令语言解析器,有自己内建的shell命令集,shell也能被系统中其他应用程序所调用。用户在提示符下输入的命令都由shell先解释然后传给linux核心。
    当普通用户成功登陆,系统将执行一个称为shell的程序,shell进程提供了命令行的提示符,对普通用户用”$”作提示符,超级用户用”#”作提示符。一旦出现shell提示符,就可以键入命令名称及命令需要的参数,shell将执行这些命令,如果命令花费太长时间运行,或屏幕产生大量输出,可以键入ctrl+c发出中断信号中断。
    shell的模拟
    运行时,程序会先进入一个死循环,直至用户输出exit时停止循环。死循环内,先用一个PutFlag()函数为程序输入命令行提示符
    在这里插入图片描述
    getpwuid()函数是通过用户的uid查找用户的passwd数据,如果出错,都返回一个空指针并设置errno的值,用户可以根据perror函数查看出错信息,utsname结构体则用来获取系统的信息,当命令行提示符打印后,用fflush冲刷掉标准输出上的内容(stdout)。
    在这里插入图片描述
    然后是接收键入的命令,由于fegts()函数会把键入的enter键收入到末尾,所以要手动把接收的命令最后一个元素置空。当命令输入完毕,对用户输入的命令做简单的解析,就是把包含命令的字符串分割成一个个单独的命令,strtok()函数会将cmd分割成以” ”为分隔符的字符串,再次调用时要将cmd设成NULL。
    在这里插入图片描述
    在这里插入图片描述
    执行命令前,用fork()创建子进程,在子进程调用execv()函数替换程序,执行键入的命令,失败时退出,当父进程调用wait(),进程立即阻塞自己,由wait自动分析是否当前进程的某个子进程已经退出,如果让它找到一个已经变成僵尸进程的子进程,wait就会手机这个子进程的信息,并把它称帝销毁后返回,如果没有找到这样一个子进程,wait就会一直阻塞在这样里,知道有一个出现为止,wait(NULL)如果成功,返回被收集的子进程的ID,如果调用进程没有子进程,调用失败,返回-1,同时errno被设置为ECHILD。

    展开全文
  • 【Linux】一步一步学Linux——初识Linux命令解析器(10)

    千次阅读 多人点赞 2019-07-09 11:01:57
    00. 目录 文章目录00. 目录01....在计算机科学中,Shell俗称壳(用来区别于核),是指“为使用者提供操作界面”的软件(命令解析器)。它类似于DOS下的command.com和后来的cmd.exe。它接收用户命令...

    00. 目录

    01. Shell简介

    在计算机科学中,Shell俗称壳(用来区别于核),是指“为使用者提供操作界面”的软件(命令解析器)。它类似于DOS下的command.com和后来的cmd.exe。它接收用户命令,然后调用相应的应用程序。

    [外链图片转存失败(img-n2EQMtPJ-1562641251457)(assets/1562638860330.png)]

    shell同时又是一种程序设计语言。作为命令语言,它交互式解释和执行用户输入的命令或者自动地解释和执行预先设定好的一连串的命令;作为程序设计语言,它定义了各种变量和参数,并提供了许多在高级语言中才具有的控制结构,包括循环和分支。

    02. Shell分类

    第一类:图形界面shell(Graphical User Interface shell 即 GUI shell)

    例如:应用最为广泛的 Windows Explorer (微软的windows系列操作系统),还有也包括广为人知的 Linux shell,其中linux shell 包括 X window manager (BlackBox和FluxBox),以及功能更强大的CDE、GNOME、KDE、 XFCE。

    第二类:命令行式shell(Command Line Interface shell ,即CLI shell)

    例如:bash / sh / ksh / csh(Unix/linux 系统)

    MS-DOS系统)cmd.exe/ 命令提示字符([Windows NT](https://baike.baidu.com/item/Windows NT) 系统)

    Windows PowerShell(支持 .NET Framework 技术的 Windows NT 系统)

    传统意义上的shell指的是命令行式的shell,以后如果不特别注明,shell是指命令行式的shell。

    文字操作系统与外部最主要的接口就叫做shell。shell是操作系统最外面的一层。shell管理你与操作系统之间的交互:等待你输入,向操作系统解释你的输入,并且处理各种各样的操作系统的输出结果。

    shell提供了你与操作系统之间通讯的方式。这种通讯可以以交互方式(从键盘输入,并且可以立即得到响应),或者以shell script(非交互)方式执行。shell script是放在文件中的一串shell和操作系统命令,它们可以被重复使用。本质上,shell script是命令行命令简单的组合到一个文件里面。

    Shell基本上是一个命令解释器,类似于DOS下的command。它接收用户命令(如ls等),然后调用相应的应用程序。较为通用的shell有标准的Bourne shell (sh)和C shell (csh)。

    03. 交互式shell和非交互式shell

    交互式shell和非交互式shell(interactive shell and non-interactive shell)
    交互式模式就是在终端上执行,shell等待你的输入,并且立即执行你提交的命令。这种模式被称作交互式是因为shell与用户进行交互。这种模式也是大多数用户非常熟悉的:登录、执行一些命令、退出。当你退出后,shell也终止了。
    非交互式模式,以shell script(非交互)方式执行。在这种模式 下,shell不与你进行交互,而是读取存放在文件中的命令,并且执行它们。当它读到文件的结尾EOF,shell也就终止了。
    可以通过打印“$-”变量的值(代表着当前shell的选项标志),查看其中的“i”选项(表示interactive shell)来区分交互式与非交互式shell。

    交互式shell

    [deng@localhost ~]$ echo $-
    himBH
    

    非交互式shell

    deng@localhost ~]$ cat test.sh 
    echo $-
    [deng@localhost ~]$ ./test.sh  
    hB
    [deng@localhost ~]$ 
    

    04. 登录shell和非登录shell

    登录shell:是需要用户名、密码登录后才能进入的shell(或者通过”–login”选项生成的shell)。
    非登录shell:当然就不需要输入用户名和密码即可打开的Shell,例如:直接命令“bash”就是打开一个新的非登录shell,在Gnome或KDE中打开一个“终端”(terminal)窗口程序也是一个非登录shell。
    执行exit命令,退出一个shell(登录或非登录shell);执行logout命令,退出登录shell(不能退出非登录shell)。

    //登录shell
    [deng@localhost ~]$ bash --login
    [deng@localhost ~]$ logout
    //登录shell
    [deng@localhost ~]$ bash --login
    [deng@localhost ~]$ exit
    登出
    
    //非登录shell
    [deng@localhost ~]$ bash
    [deng@localhost ~]$ logout
    bash: logout: 不是登录shell: 使用 `exit'
    [deng@localhost ~]$ exit
    exit
    [deng@localhost ~]$ 
    

    bash是 login shell 时,其进程名为”-bash“ 而不是”bash”。 比如下面的命令行演示:
    man bash: A login shell is one whose first character of argument zero is a -, or one started with the –login option.

    在chinaunix论坛中看到的关于登录shell与非登录shell、交互式shell与非交互式shell的讨论:
    Login Shell:当init3时,mingetty spawn出登录提示符的时候,你输入帐号和密码进入了,出现了shell提示符,这个过程就是登录shell;
    Interactive Shell:接下来你得到了一个像“root@host />”这样的提示符,你输入什么,它就解释出什么,这就是交互式shell;你在当前shell又fork了一个 shell(bash),这个bash也是交互式shell。通常情况下interactive shell是login shell派生出来的。
    Non Interactive Shell:你需要写一个bash script,用外部shell执行“bash script.sh”命令,它(bash)从可以第一条命令执行到最后一条然后退出,不与你进行任何交互,它就是非交互式shell。
    交互式非登录shell,例如你在Gnome图形界面中打开“终端”出来的那种窗口程序,和登录shell相比,它是“非登录”的,你并不需要输入用户名和密码,和非交互式shell相比,这是“交互式”的,就像你说的那它“你输入什么,它就解释什么”。

    对于Bash来说,登录shell(包括交互式登录shell和使用“–login”选项的非交互shell),它会首先读取和执行/etc/profile全局配置文件中的命令,然后依次查找/.bash_profile、/.bash_login 和 ~/.profile这三个配置文件,读取和执行这三个中的第一个存在且可读的文件中命令。除非被“–noprofile”选项禁止了。在非登录shell里,只读取 ~/.bashrc (和 /etc/bash.bashrc、/etc/bashrc )文件,不同的发行版里面可能有所不同,如RHEL7.5中非登录shell仅执行了“~/.bashrc”文件(没有执行/etc/bashrc),而Ubuntu16.04中却依次执行了/etc/bash.bashrc 和 ~/.bashrc 文件。
    对于这些规则,可以直接在相应的配置文件中加一些echo命令来验证其真实性。

    05. Shell类型

    UNIX中主要有:

    Bourne shell (包括 sh,ksh,and bash)

    Bourne shell ( sh)

    Korn shell ( ksh)

    Bourne Again shell ( bash)

    POSIX shell ( sh)

    C shell (包括 csh and tcsh)

    C shell ( csh)

    TENEX/TOPS C shell ( tcsh)

    Bourne Shell

    首个重要的标准Unix Shell是1979年底在V7 Unix(AT&T第7版)中引入的,并且以它的创始科技部基础条件平台“国家气象网络计算应用节点建设”(2004DKA50730)资助者Stephen Bourne的名字命名。Bourne shell 是一个交换式的命令解释器和命令编程语言。Bourne shell 可以运行为login shell或者login shell的子shell(subshell)。只有login命令可以调用Bourne shell作为一个login shell。此时,shell先读取/etc/profile文件和$HOME/.profile文件。/etc/profile文件为所有的用户定制环境,$HOME/.profile文件为本用户定制环境。最后,shell会等待读取你的输入。

    C Shell

    Bill Joy于20世纪80年代早期,在伯克利的加利福尼亚大学开发了C shell。它主要是为了让用户更容易的使用交互式功能,并把ALGOL风格的语法结构变成了C语言风格。它新增了命令历史、别名、文件名替换、作业控制等功能。

    Korn Shell

    有很长一段时间,只有两类shell供人们选择,Bourne shell用来编程,C shell用来交互。为了改变这种状况,AT&T的bell实验室David Korn开发了Korn shell。ksh结合了所有的C shell的交互式特性,并融入了Bourne shell的语法。因此,Korn shell广受用户的欢迎。它还新增了数学计算,进程协作(coprocess)、行内编辑(inline editing)等功能。Korn Shell 是一个交互式的命令解释器和命令编程语言.它符合POSIX——一个操作系统的国际标准.POSIX不是一个操作系统,而是一个目标在于应用程序的移植性的标准——在源程序一级跨越多种平台。

    Bourne Again Shell (bash)

    bash是GNU计划的一部分,用来替代Bourne shell。它用于基于GNU的系统如Linux.大多数的Linux(Red Hat,Slackware,Caldera)都以bash作为缺省的shell,并且运行sh时,其实调用的是bash。

    POSIX Shell

    POSIX shell 是Korn shell的一个变种. 当前提供POSIX shell的最大卖主是Hewlett-Packard。在HP-UX 11.0,POSIX shell 就是/bin/sh,而bsh是/usr/old/bin/sh.

    各主要操作系统下缺省的shell:

    AIX下是Korn Shell.

    Solaris缺省的是Bourne shell.

    FreeBSD缺省的是C shell

    HP-UX缺省的是POSIX shell.

    Linux是Bourne Again shell.

    [Windows PowerShell](https://baike.baidu.com/item/Windows PowerShell)是一种新的交互式的命令行和基于任务脚本编写技术,它使信息技术(IT) 管理员能够全面地自动操作和控制系统管理任务,从而提高了管理员的生产力。Windows PowerShell 包括多个系统管理实用工具、一致的语法和命名惯例、及对普通管理数据更好地导航,如登记、证书存储 或 Windows Management Instrumentation (WMI)。Windows PowerShell 还专门针对 IT 管理,提供直观的脚本编写语言。

    06. 参考

    百度百科
    参考:【Linux】一步一步学Linux系列教程汇总

    展开全文
  • SimpleCommandParser(简单的命令解析器) 2. 开发目的 熟悉Linux编程环境,加强对Linux命令的理解及函数的运用。 3. 开发环境 LSB Version: :core-3.0-ia32 :core-3.0-noarch :graphics-3.0-ia32 :...

    一。项目概述


    1. 项目名称

    SimpleCommandParser(简单的命令解析器)

    2. 开发目的

    熟悉Linux编程环境,加强对Linux命令的理解及函数的运用。

    3. 开发环境

    LSB Version:  :core-3.0-ia32
    :core-3.0-noarch
    :graphics-3.0-ia32
    :graphics-3.0-noarch
    Distributor ID:  RedHatEnterpriseAS
    Description:    Red Hat Enterprise Linux AS release 4 (Nahant Update 4)
    Codename:     NahantUpdate4
    Compiler:      GNU GCC Compiler (mingw32-g++.exe)
    Programming Language: C++

    4. 文件说明

    ① 服务器路径:    /home/10111/project
    ② 程序测试目录:  /home/10111/project/LastTest/
    ③ 程序运行文件:  /home/10111/project/LastTest/RunProgram
    ④ 源程序文件夹:  /home/10111/project/SimpleCommandParser
    ⑤ 程序制作相关:  /home/10111/project/About

    5. 项目功能 (已完成)

    ① 显示当前所在目录的路径名的指令:pwd
    ② 列出指定目录名中的所有目录及文件的指令:dir  <目录名>
    ③ 改变当前工作目录的指令:cd  <目录名或路径>
    ④ 显示当前日期的指令:date
    ⑤ 在指定的目录及其子目录中查找指定的文件的指令:
    find <目录>  -name <待查找的文件名>
    ⑥ 重命名一个文件或目录的指令:
    rename  <旧文件名1> <新文件名1> <旧文件名2> <新文件名2>…
    ⑦ 新建目录的指令:
    newdir  <目录名1> <目录名2> …<目录名n>
    ⑧ 删除目录的指令
    deldir   <目录名1> <目录名2> …<目录名n>
    ⑨ 退出命令解释程序的指令:exit

    6.项目特点

    ① 强扩展性:可以方便地根据需求进行指令的增删修改。
    ② 强容错性:可以接受用户各种方式的错误指令输入。
    ③ 可维护性:项目模块间耦合性低,可以轻松维护项目。
    ④ 简单易用:各指令符合用户日常使用习惯。

    7. 参考文献或书籍资料

    ① Unix技术网:http://man.chinaunix.net/
    ② 百度百科: http://baike.baidu.com/
    ③ GNU Operating System:http://www.gnu.org/



    二。程序使用演示


    1. 程序启动

    如上图所示,程序启动后,用户能看见友好欢迎界面,欢迎界面上简要地列出了本程序所支持的指令及指令的基本用法。

    2. 显示当前日期(date)指令演示

    如上图所示,直接在程序中输入指令“date”,程序即可显示当前日期,如果在“date”指令后面(输入空格后)再输入多余的字符串,程序将会忽略后面的字符串,照常执行“date”指令。

    3. 显示当前所在目录的路径名的(pwd)指令演示

    如上图所示,直接在程序中输入指令“pwd”,程序即可显示当前工作目录路径,如果在“pwd”指令后面(输入空格后)再输入多余的字符串,程序将会忽略后面的字符串,照常执行“date”指令。

    4.列出指定目录名中的所有目录及文件的指令( dir )指令演示

    如上图所示,如需浏览某一目录内的内容,用户可先输入“dir”然后输入想要浏览的目录,输入的目录参数使用空格间开,按下回车后,程序即可列出当前工作目录中的所有文件(并显示出当前遍历的目录)。

    如上图所示,如果在“dir”指令后不添加任何参数,程序将路径默认设为正在工作的目录。

    5. 新建目录的指令 (newdir) 演示

    如上图所示,如需新建某一目录时,用户可先输入“newdir”然后输入需创建的目录名,输入的目录参数使用空格间开,按下回车后,若跳到下一指令输入语句,即代表命令已成功执行,每次操作可通过dir命令进行验证。如上图,在输入newdir指令后用dir可发现文件夹“FirstTestDirectory”已成功创建,另外,该指令还支持同时接受多个参数,即可同时创建多个文件夹,创建多个文件夹时只需用空格间开每个文件夹名即可(见图中的“SecondTestDirectory”和“ThirdTestDirectory”文件夹的创建)。注意,当需要新建的文件夹名己存在,程序会提示用户文件夹创建失败。

    6. 删除目录的指令 (deldir) 演示

    如上图所示,如需删除某一目录时,用户可先输入“deldir”然后输入需删除的目录名,输入的目录参数使用空格间开,按下回车后,若跳到下一指令输入语句,即代表命令已成功执行,每次操作可通过dir命令进行验证。如上图,在输入deldir指令后用dir可发现文件夹“FirstTestDirectory”已成功删除,此外,该指令同样支持同时接受多个参数,即可同时删除多个文件夹,删除多个文件夹时只需用空格间开每个文件夹名即可(见图中的“SecondTestDirectory”和“ThirdTestDirectory”文件夹的删除)。注意,当需要删除的文件夹不存在,程序会提示用户目录删除失败。

    7. 重命名一个文件或目录的指令 (rename) 演示

    如上图所示,若想将目录名为1的目录更改为a,只需输入rename指令,加上1(旧文件名)和a(新文件名),(指令及文件名之间使用空格间开,)即可完成重命令功能,若想同时更改多个文件的名字,只需要在输入参数的时候以旧文件名,新文件名,旧文件名,新文件名…的格式输入即可完成对应功能,若没发现需要重命名的文件,则程序会给出错误提示。

    8. 改变当前工作目录的指令 ( cd ) 演示

    如上图所示,若想更改当前工作目录,直接输入“cd”命令后跟路径名即可,如果想返回上一级目录,可输入“cd .. ”,如输入的路径有误,会停留在当前工作目录,并且为用户给出提示信息。

    9. 在指定的目录及其子目录中查找指定的文件的指令 ( find ) 演示

    如上图所示,find指令可以找出在指定的目录及其子目录中查找指定的文件,在找到查找目标文件后,会列出文件所在目录的完整路径,若不输入第一个参数,程序会默认在本目录开始寻找目标文件,如上例所示,在目录“/home/10111/project/LastTest”中输入“find /home/10111/project/LastTest –name file1.txt”和“find –name file1.txt”能达到同样的查找结果。若未能找出文件,程序会自动提示用户“目录不存在或没有在指定目录中找到文件”。

    10. 退出命令解释程序的指令 ( exit ) 演示

    如上图所示,当输入退出命令“exit”后,程序显示出退出的友好提示信息后会自动返回到Linux终端。



    三。概念原理


    1. 显示当前所在目录的路径名的指令依赖函数

    简要介绍

    getcwd(取得当前的工作目录)

    相关函数

    get_current_dir_name,getwd,chdir

    表头文件

    #include<unistd.h>

    定义函数

    char * getcwd(char * buf,size_t size);

    函数说明

    getcwd()会将当前的工作目录绝对路径复制到参数buf所指的内存空间,参数size为buf的空间大小。在调用此函数时,buf所指的内存空间要足够大,若工作目录绝对路径的字符串长度超过参数size大小,则回值NULL,errno的值则为ERANGE。倘若参数buf为NULL,getcwd()会依参数size的大小自动配置内存(使用malloc()),如果参数size也为0,则getcwd()会依工作目录绝对路径的字符串程度来决定所配置的内存大小,进程可以在使用完此字符串后利用free()来释放此空间。

    返回值

    执行成功则将结果复制到参数buf所指的内存空间,或是返回自动配置的字符串指针。失败返回NULL,错误代码存于errno。

    2. 列出指定目录名中的所有目录及文件的指令依赖函数

    简要介绍

    readdir(读取目录)

    相关函数

    open,opendir,closedir,rewinddir,seekdir,telldir,scandir

    表头文件

    #include<sys/types.h>

    #include<dirent.h>

    定义函数

    struct dirent * readdir(DIR * dir);

    函数说明

    readdir()返回参数dir目录流的下个目录进入点。

    结构dirent定义如下

    struct dirent

    {

    ino_t d_ino;

    ff_t d_off;

    signed short int d_reclen;

    unsigned char d_type;

    har d_name[256;

    };

    d_ino 此目录进入点的inode

    d_off 目录文件开头至此目录进入点的位移

    d_reclen _name的长度,不包含NULL字符

    d_type d_name 所指的文件类型

    d_name 文件名

    返回值

    成功则返回下个目录进入点。有错误发生或读取到目录文件尾则返回NULL。

    附加说明

    EBADF参数dir为无效的目录流。

    3. 改变当前工作目录的指令依赖函数

    简要介绍

    chdir(改变当前的工作(目录)

    相关函数

    getcwd,chroot

    表头文件

    #include<unistd.h>

    定义函数

    int chdir(const char * path);

    函数说明

    chdir()用来将当前的工作目录改变成以参数path所指的目录。

    返回值

    执行成功则返回0,失败返回-1,errno为错误代码。

    执行

    current working directory :/tmp

    4. 显示当前日期的指令依赖函数

    简要介绍

    localtime(取得当地目前时间和日期)

    相关函数

    time, asctime, ctime, gmtime

    表头文件

    #include<time.h>

    定义函数

    struct tm *localtime(const time_t * timep);

    函数说明

    localtime()将参数timep所指的time_t结构中的信息转换成真实世界所使用的时间日期表示方法,然后将结果由结构tm返回。结构tm的定义请参考gmtime()。此函数返回的时间日期已经转换成当地时区。

    返回值

    返回结构tm代表目前的当地时间。

    5. 在指定的目录及其子目录中查找指定的文件的指令依赖函数

    简要介绍

    ftw(遍历目录树)

    头文件

    #include <ftw.h>

    函数定义

    int ftw(const char *dir, int (*fn)(const char *file, const struct stat *sb, int flag), int depth);

    函数说明

    ftw()会从参数dir指定的目录开始, 往下一层层地递归式遍历子目录. 每进入一个目录, 便会调用参数*fn定义的函数来处理. ftw()会传三个参数给fn(), 第一个参数*file指向当时所在的目录路径, 第二个参数是*sb, 为stat结构指针(该结构的定义请参考stat()), 第三个参数为旗标, 有下面几种可能值:

     

       FTW_F    一般文件

     

       FTW_D    目录

     

       FTW_DNR 不可读取的目录. 此目录以下将不被遍历

     

       FTW_SL   符号连接

     

       FTW_NS   无法取得stat结构数据, 有可能是权限问题

     

    最后一个参数depth代表ftw()在进行遍历目录时可同时打开的文件数. ftw()在遍历时每一层目录至少需要一个文件描述词, 如果遍历时用完了depth所给予的限制数目, 整个遍历将因不断地关闭文件和打开文件操作而显得缓慢. 如果要结束ftw()的遍历, fn()只需返回一非零值即可, 此值同时也会是ftw()的返回值, 否则ftw()会试着走完所有的目录, 然后返回0. 遍历中断则返回fn()函数的返回值, 全部遍历完则返回0. 如有错误发生则返回-1.

    附加说明

    由于ftw()会动态配置内存使用, 请使用正常方式(fn函数返回非0值)来中断遍历, 不要在fn函数中使用longjmp().

    6. 重命名一个文件或目录的指令依赖函数

    简要介绍

    rename(更改文件名称或位置)

    相关函数

    link,unlink,symlink

    表头文件

    #include<stdio.h>

    定义函数

    int rename(const char * oldpath,const char * newpath);

    函数说明

    rename()会将参数oldpath 所指定的文件名称改为参数newpath所指的文件名称。若newpath所指定的文件已存在,则会被删除。

    返回值

    执行成功则返回0,失败返回-1,错误原因存于errno

    7. 新建工作目录的指令依赖函数

    简要介绍

    mkdir(创建工作目录)

    头文件

    #include <sys/stat.h>

     

    #include <sys/types.h>

    定义函数

    int mkdir(const char *pathname, mode_t mode);

    函数说明

    mkdir()函数以mode方式创建一个以参数pathname命名的目录,mode定义新创建目录的权限。

    返回值

    若目录创建成功,则返回0;否则返回-1,并将错误记录到全局变量errno。

    8. 删除目录的指令依赖函数

    简要介绍

    rmdir(删除工作目录)

    头文件

    #include <unistd.h>

    定义函数

    int rmdir(const char *filename)

    函数说明

    这函数删除一个工作目录。这工作目录在执行删除功能前必须为空。当然,有两种例外情况也会不能成功删除文件,在errno中包含了这两种情况,是:ENOTEMPTY,二是EEXIST,这两种错误情况是一样的,有些系统使用第一种提示,有些系统使用第二种提示。GNU/Linux和GNU/Hurd系统常常使用ENOTEMPTY。



    四。模块设计


    1. 设计概要

    本次项目的题为简单的命令解释器,显然,单个命令处理流程可以简单地分为下图几个步骤:

    那么,要想解释一条指令,最开始第一步就是用户输入指令并由程序读取指令,第二步是由程序分析指令,第三步程序对分析好的指令进行处理,第四步将指令的处理结果输出。

    而用户对指令的输入和程序对指令的存储与分析紧密相联,要求用户输入指令及读取指令的方法比较简单,并且在读取指令及分析指令阶段时所产生的大部分临时数据并不需要在处理指令和输出结果这两步中用到,这样可以降低第一二步与第三四步操作的耦合性,因此将读取指令和分析指令封装为一个类进行处理;至于处理指令和输出结果两步,在本次“简单命令解释器”项目中,结果的反馈信息并不多,如在指令执行成功后可直接不输出结果即可向用户表明结果处理成功,在指令执行失败后只需给一句话或一段话以提示用户错误原因,因此,如果将输出结果也独立出一个类就使程序有点冗余了,如下图所示:

    可以先将项目项分为这两个类来管理,第一个类用于对指令的读取及解释,第二个类用于对指令的处理和结果的汇报,两个类间的关联只需要是一个规范化好的指令格式数据即可。

    如上图所示,事实上,上述一直所提到的四个步骤都只是对一条指令的处理,而这次的“简单命令解释器”项目不可能只处理一条指令就结束程序,因此,我们可以再将四个步骤(两个大类)进行封装,使用一个控制类来总管理它们的各种操作。

    最后,补充说明为什么将这次的项目程序编写分为三个大类来处理而不直接使用结构化的过程处理这次的项目。这是因为考虑到了这个项目汲及的指令条数比较多,如果单条指令使用单个流程,那么最终出来的程序就很有可能变得只是由几个函数组成的程序,很不好管理,对于后期的维护扩展与维护更是带来不少困难。下面可以由UML类图看出这样分模块处理的优点。

    2. UML类图

     

    如上图所示,本次项目共用三个自定义类来处理。其中,Client代表有C++的main入口函数的源文件,负责管理Controler类对象的生命周期及在生命周期间调用Controler类对象的相关函数,从而控制整个项目的执行流程。Controler类作为指令的分析及处理的综合管理类,可以从上面“设计概要”说明中体现这个类的重要性,使用这个类可以方便综合管理用户输入指令并由程序读取指令,程序分析指令,程序对分析好的指令进行处理,程序将指令的处理结果输出这四个程序执行步骤,为了达到管理目的,它需要依赖私有数据成员CommandAnalysis和FunctionImplement类。CommandAnalysis类负责要要求用户输入指令和读取指令并将指令规范化。FunctionImplement类负责处理已经规范化好的指令及将指令处理结果输出。



    五。详细设计


    1. 在CommandAnalysis类中指令的预定义

    规则:在CommandAnalysis类中,使用了多个set<string>数据结构对指令进行预存储,而它们的变量名分别为 comm0p,comm1p,comm1np,comm1p_0p,comm2pn,comm1p1o1p_1o1p,optionComm,在这些命名当中,comm代表了命令名称,例如:“dir”,“pwd”和“cd”这些命令等等,而紧跟在comm后的数字和字母p(parameter)代表的是对应comm命令后所需附加的参数个数,如果有n表示,则代表可以跟数字的n倍个参数,下划线则代表“或”,即comm命令可以加下划线前面格式的参数,也可以加下划线后面格式的参数。optionComm代表一些选项关键字,它一般是在comm后面的。

    意义:将这些命令分别存放于set<string>类的变量中,这是为了区分不同格式类型的命令,也就是说,我们可以通过这些不同的变量名而给我们需要执行的命令进行分类从而能正确地解析出命令,这也体现了程序的可扩展性,当我们需要添加需要解析的指令时,如果存在同一类型的指令,只需在程序中这个类的初始化时再添加新指令,程序就能解析这个指令而不用添加过多的代码了。

    2. 在CommandAnalysis类中命令的规范化

    规则:在上面的说明中,我们常常提到了将命令格式化或规范化,那么,不得不说一下,这里的规范化,指的是我自定义的一种存放指令及指令参数的一种格式,由上面的类图中可以看出,CommandAnalysis类中有一个成员变量vector<string> resultList,resultList容器中的第一个元素(即resultList[0])用于存放命令名称,而容器中的第一个元素以下(如resultList[1]...resultList[n])的元素则用于存放命令的参数。

    方法:在规范化时,我们使用的是bool CommandAnalysis::formatCommand()函数,该函数先从用户输入的字符串string userCommand中使用getNextWord()函数获取第一个单词临时存放于string currentStr中,然后使用if-else语句判断currentStr是属于上面第一小点所提到的多个set<string>中的哪一个变量中的值,如果不属于这些set集当中,则提示错误信息并返回false,否则跳转到对应的格式处理函数,如:当currentStr存在于set<string> comm1p中,则执行bool CommandAnalysis::judgeComm1p(),若当currentStr存在于变量set<string> comm0p中,则执行bool CommandAnalysis::judgeComm0p(),通过调用相对应的函数进一步对命令进行规范化。在类似命令judgeComm ()系列的函数,它们是通过逐步获取用户输入串string userCommand中的单词(以空格或\t分隔的字符串),以各自的命令规则,将所需要的参数放进resultList容器尾中,而在这规范化的过程中,程序并不会识别这个参数是否正确,参数(如:文件路径)的正确与否会在函数功能的实现时才进行判断。因此,最终的用户串输入规范化结果就会保存在resultList容器中了。

    3. 在CommandAnalysis类中字符串的单词获取

    参数依赖及解析:我们将单词的获取函数在CommandAnalysis类中声明为bool CommandAnalysis::getNextWord(),该函数在CommandAnalysis类中,并且会依赖私有数据成员string userCommand,string currentStr和long currentIndex完成单词读取的功能,当然,这里的单词,与上文所指的一样,是在userCommand字符串中再由' ','"'或‘\0'分隔的字符串。currentStr变量的作用就是临时存放本次单词获取的结果。在这里需要知道的是,我们这里不断提及到的单词获取是能过不断读取与比较userCommand字符串中的单个字符完成的,每读取一个字符,currentIndex变量就会加1,以记录当前读取的字符位和下一位该读取的字符位。

    方法:实际上,单词的读取还需要我们去考虑一些边界条件,首先,我们得知道什么时候我们所读取的字符可能会溢出,这里所指的溢出,我们可以考虑到字符串的边界,因此,作为进入函数后,将currentIndex(可读取字符下标)加1(移到下一位读取字符)后,首先得判断是否越出了userCommand的字符串的长度(即所说的边界),若不会越出边界,即可以进行正常读取。

    而在读取时,我们首先得考虑将空格符去除,因为本次项目中的空格符是作为单词的分界符,去除空格符,可以使用一个while循环语句进行操作,当读到空格符时,将currentIndex加1,判断如果currentIndex没有越界,则继续进行循环,直到不再遇到空格符为止。

    接下来,我们还需要考虑一种情况,有些参数是允许带空格符的,不过,在这些允许带空格符的单词中,会有通过一种特殊的输入符(“”)双引号进行辨认,也就是说,我们有只要双引号内的字符串,我们都称为单词,读取双引号单词,我们可以先判断正在读取的字符是否双引号,若是,则继续进行判断,currentIndex加1并进行越界判断,如果加了1后的currentIndex处的字符再次为双引号则证明这是一个空单词,并返回错误,结束函数,如果currentIndex处的字符是空格或者字符串结束符,那么这也是一个错误的单词,如果即不是双引号,又不是空格和结束符,就将字符存储并且继续加1移动currentIndex,判断是否合法字符,存储,循环操作直到遇到第二个引号即结束本次单词读取并使该函数结果返回“true”。

    最后,如果单词不是以双引号作为开始标记,以空格或结束符为结束标记,我们则可以开始正常逐个字符读取及存储,使用空格或结束符作为字符的读取循环结束标记。

    4. 在FunctionImplement类中指令功能选择实现的入口函数

    概念:指令功能选择实现的入口函数指的是能判断用户己规范化好的指令类型和能实现在函数内自动进行指令方法调用的公有函数。该函数在类FunctionImplement中函数原型是:int start(vector<string> comm)。comm作为字符串容器,容器内元素第0位存放指令名称,其它位存放指令参数。函数返回它所调用指令的代号: -1:NoCommand, 0:exit, 1:pwd, 2:dir, 3:cd, 4:newdir, 5:deldir, 6:rename, 7:find, 8:date。函数接受空的vector<string>和未规范化的指令容器,当遇到上述两种情况时,函数内部会自动进行判断并返回结果。

    方法:进入函数后,首先判断comm容器是否为空,若为空,则输出错误信息,函数返回false,若非空,则继续判断,使用if-else分支结构语句判断comm[0]与指令名“pwd”、“dir”、“newdir”等是否相同,若都不相同,则输出错误提示信息,函数返回false,若相同,则在if语句块中调用对应的功能函数,如:if("pwd" == comm[0]){myPwd();},当遇上可接受n个参数的指令时,则使用for循环语句重复功能函数的调用。

    5. 在FunctionImplement类中指令功能的实现函数

    在这次指令功能的实现,大多数指令功能都直接调用Linux-C的API,而项目中,在功能实现时,除了调用API外,还记录函数处理结果,并且当功能处理失败时,给出提示信息。如:新建目录的部分函数代码:

    int res = mkdir(addr.c_str(), S_IRWXU);
    if(0 != res){
          printf("提示信息:创建%s目录失败,", addr.c_str());
          switch(errno){
              case EACCES: printf("不具有写入操作的权限。\n");break;
              …(此处省略代码)
          }
    }
    

    这里先是调用了新建目录的库函数,然后临时保存返回值,紧接着对返回值进行判断,如果返回值非零的话,说明函数执行不成功,有错误信息,在if语句中再对错误信息进行处理,如果errno的值为EACCES,则根据API参考文档,它意思是“不具有写入操作的权限”,此时我们将这提示信息输出,将该信息反馈给用户。

    6. 在FunctionImplement类中查找功能的实现

    原因:在第5小点中说到了大多数功能的实现都是直接调用库函数,而这里特意提到查找功能的实现的原因是它并非只调用一个库函数就可以实现功能。

    依赖:查找功能的实现,用到了两个公有成员变量,string targetName和int targetNum,一个静态成员变量static FunctionImplement * myObject还有一个私有成员函数 void myFind(string dirStr, string fileName)和一个公有成员函数int myFindAssistant2(const char * findName);以及一个全局函数int myFindAssistant(const char *file, const struct stat* sb, int flag)。

    实现:实现查找功能的入口函数是私有成员函数 void myFind(string dirStr, string fileName),进入该函数后,首先检查dirStr目录是否为空,如果为空,则默认检测当前工作目录,把dirStr设为当前工作目录,然后,将myObject静态数据成员的值设为this指针的地址,目的是能使该对象在全局函数中使用,然后将,公有数据成员targetName设为fileName的值,这样可以让公有成员函数int myFindAssistant2(const char * findName)确定查找目标,将targetNum设为0,找到目标文件的数目为0,准备工作做完后,可以开始调用Linux-C的API函数ftw, ftw(dirStr.c_str(), myFindAssistant, 500),ftw()会从参数dirStr指定的目录开始, 往下一层层地递归式遍历子目录. 每进入一个目录, 便会调用myFindAssistant(…)全局函数来处理。接下来我们看int myFindAssistant(const char *file, const struct stat* sb, int flag)全局函数,在这函数中我们可以直接使用参数flag判断当前char* file是否文件,如果(flag == FTW_F)则开始调用函数FunctionImplement::myObject -> myFindAssistant2(file),该函数对单个文件的判断,判断传入参数路径是否需寻找的目标文件,先分析传入参数先分析路径,如是根路径,则直接与targetName进行对比,如非根路径,则将文件名提取出来后与targetName对比,若找到与目标文件名targetName相等,则输出路径,并将targetNum加1。就这样,将两个公有成员变量,一个静态成员变量,一个私有成员函数,一个公有成员函数,一个全局函数联合起来,实现了文件查找的功能。



    六。总结


    开篇:

    本次项目主题,是操作系统的课程设计题目,刚刚接触到题目那刻,还是觉得题目是相当地有难度的,题为“在Linux环境下模拟实现简单命令解释器”。之后,再细看下面的课程设计的要求和提示,不能调用System()函数,但是有一些功能函数提供给我们。这样一来,我明白了原来这些功能的实现都只需要调用Linux C里面的API就可以。因此,我知道了本次的项目难点不在功能的实现,而是在用户输入指令的规范化和整个项目的模块设计。

    过程:

    在知道了项目侧重点后,先大致地将项目划分为三块,一块是指令分析,一块是功能实现,三是项目整合。功能实现:对于功能实现一块,认为还是比较简单的,于是就先去了解我所需要实现功能所需要用上的API,搜集准备资料,之后就开始了FunctionImplement类的设计及编写,并在一天内将这个关于功能实现类的代码编写和简单调试完成了,编写调试过程中也没有出现“疑难杂症”。

    指令分析:

    由于刚学完编译原理理论课不久,想去利用所学的知识完成这次指令分析类的编写,刚开始时,一直在苦想如何使用编译原理的语法分析,词法分析的方法完成这次项目的指令分析,还拿起了编译原理的书本进行复习,复习的时候时刻与自己这次所做的项目进行结合思考,还上网找了一些关于写词法分析器的方法作为思路参考,后来,将这次所需要用的指令语法写出了正则表达式,画出了DFA(有穷自动机),画出了符号表,在观察这些正则表达式和符号表的时候才发现,如果这次用这种方法去完成这项目的话,未免将简单的问题复杂化了,原本可以高效分析出的指令也会变得累赘,不过还是不很相信自己所想的,因为毕竟是数天的精力才将正则表达式、DFA和符号表,数据结构定下来,忽然间放弃这种思路总觉得有点不是很值得,于是就去请教老师,结果老师很不支持这种做法,最后还是决定了放弃这种复杂的做法,直接用直接的方法,逐步获取单词来分析指令。确定了方法后,先定下这次编写这个指令分析类所需要使用的数据结构,完成类的设计,然后再开始代码的编写,这过程也是用了一天左右的时间完成。

    项目整合:

    最后,在己做好功能实现和指令分析后再进行项止的整合并不难,只需要给功能实现的类加上入口函数,给指令分析的类加上分析结果的传送,再新建一个Controler控制类,创建一个main函数对全局进行控制就完成了。在完成后,进行功能的测试,进行功能的强化改良,这过程再花上了大概一整天的时间将其完成。

    说明书编写:

    通常在我在项目设计的最后阶段,就是文档的编写和总结,这次在文档编写过程中,写说明书是最耗时间的一个阶段,它并不需要像设计项目时候那样寻求新方法去解决问题,但是会需要我们如何将自己的项目思路,项目概念表达给看说明书的人,这需要一定的概括能力及项目组织思路,因此写说明书也是一个极具挑战的工作。

    小结:

    通过这次项目的设计与编写,终于将暂时放下了两个月的C++编程重新熟悉了一次,这学期由于各种学科的学习,放下了C++一段时间,刚刚接触项目时竟然还忘了部分的C++语言基础,通过这次项目的编写,找回了一些C++的编程手感,项目设计的思路,还再次加强熟悉Linux编程环境,加强了对Linux命令的理解及函数的运用。

    附:

    本次小项目代码量:1049行

    项目最终完成日期:2012年12月15日

    项目作者:Neicole

    联系方式:http://blog.csdn.net/neicole




    七。源码

    readme.txt

    1. 项目名称
       SimpleCommandParser(简单的命令解析器)
    2. 开发目的
       熟悉Linux编程环境,加强对Linux命令的理解及函数的运用。
    3. 开发环境
       LSB Version:  :core-3.0-ia32
       :core-3.0-noarch
       :graphics-3.0-ia32
       :graphics-3.0-noarch
       Distributor ID:  RedHatEnterpriseAS
       Description:    Red Hat Enterprise Linux AS release 4 (Nahant Update 4)
       Codename:     NahantUpdate4
       Compiler:      GNU GCC Compiler (mingw32-g++.exe)
       Programming Language: C++
    4. 文件说明
       ① 服务器路径:   /home/10111/project
       ② 程序测试目录: /home/10111/project/LastTest/
       ③ 程序运行文件: /home/10111/project/LastTest/RunProgram
       ④ 源程序文件夹: /home/10111/project/SimpleCommandParser
       ⑤ 程序制作相关:  /home/10111/project/About
    5. 项目功能 (已完成)
       ① 显示当前所在目录的路径名的指令:pwd
       ② 列出指定目录名中的所有目录及文件的指令:dir  <目录名>
       ③ 改变当前工作目录的指令:cd  <目录名或路径>
       ④ 显示当前日期的指令:date
       ⑤ 在指定的目录及其子目录中查找指定的文件的指令:
          find <目录>  -name <待查找的文件名>
       ⑥ 重命名一个文件或目录的指令:
          rename  <旧文件名1> <新文件名1> <旧文件名2> <新文件名2>…
       ⑦ 新建目录的指令:
          newdir  <目录名1> <目录名2> …<目录名n>
       ⑧ 删除目录的指令
          deldir   <目录名1> <目录名2> …<目录名n>
       ⑨ 退出命令解释程序的指令:exit
    6. 项目特点
       ① 强扩展性:可以方便地根据需求进行指令的增删修改。
       ② 强容错性:可以接受用户各种方式的错误指令输入。
       ③ 可维护性:项目模块间耦合性低,可以轻松维护项目。
       ④ 简单易用:各指令符合用户日常使用习惯。
    7. 参考文献或书籍资料
       ① Unix技术网:http://man.chinaunix.net/
       ② 百度百科:http://baike.baidu.com/
       ③ GNU Operating System:http://www.gnu.org/
    8.关于
       作者:Neicole
       日期:2012.12.15
       联系:http://blog.csdn.net/neicole
    


     

    Controler.h

     

    /**
     * 类名:Controler
     * 项目名:SimpleCommandParser
     * 项目简介:在Linux环境下模拟实现简单命令解释器。
     *            包括:date, pwd, dir, cd, newdir, deldir, exit, rename, find。
     * 运行环境:在windows平台只能执行部分功能,而在Liunx环境能执行该程序的所有功能。
     * 类功能:控制整合命令输入到命令分析,结果输出过程行为的控制类。
     * 类依赖:依赖FunctionImplement作为功能实现的类和CommandAnalysis作为指令输入控制的类。
     * 类方法:在类中提供了基本构造方法(无参)和获取用户输入的函数和命令启动的函数。
     * 作者名:Neicole
     * 联系方式:http://blog.csdn.net/neicole
     **/
    
    #ifndef CONTROLER_H_
    #define CONTROLER_H_
    
    #include "CommandAnalysis.h"
    #include "FunctionImplement.h"
    
    class Controler
    {
        private:
        FunctionImplement * functionImplement;
        CommandAnalysis * commandAnalysis;
    
        public:
        Controler();            // 构造函数
        ~Controler();           // 析构函数
        bool getTrueCommand();  // 获取用户输入指令
        int startCommand();     // 命令启动
    };
    
    #endif  // CONTROLER_H_
    


     

    FunctionImplement.h

    /**
     * 类名:FunctionImplement
     * 项目名:SimpleCommandParser
     * 项目简介:在Linux环境下模拟实现简单命令解释器。
     *            包括:date, pwd, dir, cd, newdir, deldir, exit, rename, find。
     * 运行环境:在windows平台只能执行部分功能,而在Liunx环境能执行该程序的所有功能。
     * 类功能:作为命令解释的类,提供了每个命令解释的方法执行及结果处理。
     * 类依赖:使用了STL库中的vector、string基本数据结构类。
     * 类成员:本类没有整个类需要使用上的私有成员变量,
     *          使用了一个功能实现的函数入口方法,int start(vector<string>);
     *          通过该方法可以判断用户己格式化好的指令类型及自动进行方法调用。
     * 作者名:Neicole
     * 联系方式:http://blog.csdn.net/neicole
     **/
    
    #ifndef FUNCTIONIMPLEMENT_H_
    #define FUNCTIONIMPLEMENT_H_
    
    #include <vector>
    #include <string>
    
    using std::string;
    using std::vector;
    
    class FunctionImplement
    {
        public:
        // 基本构造及析构函数
        FunctionImplement();
        ~FunctionImplement();
    
        // 本类中所有功能实现的函数入口
        int start(vector<string>);
    
        // 作为辅助查功功能的一些成员变量及函数
        static FunctionImplement * myObject;
        string targetName;
        int targetNum;
        int myFindAssistant2(const char * findName);    // 查找功能的辅助函数
    
        private:
        // 指令功能函数
        void myPwd();          //显示当前所在目录的路径名
        void myDir(string);    //列出指定目录名中的所有目录及文件
        void myCd(string);     //改变当前工作目录
        void myNewdir(string); //新建目录
        void myDeldir(string); //删除目录
        void myExit();         //退出命令解释程序
        void myRename(string oldName,  string newName); //重命名一个文件或目录
        void myFind(string dirStr, string fileName);    //在指定的目录及其子目录中查找指定的文件
        void myDate();                                  //显示当前日期
    };
    
    #endif  // FUNCTIONIMPLEMENT_H_
    


    CommandAnalysis.h

    /**
     * 类名:CommandAnalysis
     * 项目名:SimpleCommandParser
     * 项目简介:在Linux环境下模拟实现简单命令解释器。
     *            包括:date, pwd, dir, cd, newdir, deldir, exit, rename, find。
     * 运行环境:在windows平台只能执行部分功能,而在Liunx环境能执行该程序的所有功能。
     * 类功能:可以让用户输入指令及对用户所输入的指令进行判断及格式化。
     * 类依赖:使用了STL库中的vector、set、string基本数据结构类。
     * 类成员:使用多个set私有成员分别存放不同的规范格式的指令集;
     *            使用string对象存放用户输入的指令,vector<string>存放指令分析结果;
     *            而string CurrentStr则作为分析指令时的临时数据存放变量,
     *            long CurrentIndex作为当前指令分析(规范化)在用户输入指令字符串中的临时标志位。
     * 作者名:Neicole
     * 联系方式:http://blog.csdn.net/neicole
     **/
    
    #ifndef COMMANDANALYSIS_H_
    #define COMMANDANALYSIS_H_
    
    #include <string>
    #include <vector>
    #include <set>
    
    using std::set;
    using std::string;
    using std::vector;
    
    class CommandAnalysis
    {
        private:
        // 预定义指令
        set<string> comm0p;
        set<string> comm1p;
        set<string> comm1np;
        set<string> comm1p_0p;
        set<string> comm2pn;
        set<string> comm1p1o1p_1o1p;
        set<string> optionComm;
    
        // 输入及字符串相关
        string userCommand;            // 用户所输入的指令
        vector<string> resultList;     // 分析结果完整指令
        string currentStr;             // 正在分析的指令
        long currentIndex;              // 分析时最后读取到的指令
    
        // 功能函数
        void constructComm();           // 初始化预定义指令
        void startPrepare();            // 在开始分析用户指令前所做相应的准备
        void saveAndClearCurString();   // 清空并且保存当前临时分析出来的单个单词
        bool getNextWord();             // 从用户输入串中获取下一个单词段
    
        // 规范化用户输入指令时的判断函数
        bool judgeComm0p();
        bool judgeComm1p();
        bool judgeComm1np();
        bool judgeComm1p_0p();
        bool judgeComm2pn();
        bool judgeComm1p1o1p_1o1p();
    
        public:
        CommandAnalysis();      // 构造函数
        ~CommandAnalysis();     // 析构函数
    
        void getUserCommand();  //获取用户输入指令
        bool formatCommand();   // 命令规范化
    
        vector<string> getResultList();     // 获取分析结果
    };
    
    #endif // COMMANDANALYSIS_H_
    

     

    main.cpp

    /**
     * 文件名:main.cpp
     * 项目名:SimpleCommandParser
     * 项目简介:在Linux环境下模拟实现简单命令解释器。
     *            包括:date, pwd, dir, cd, newdir, deldir, exit, rename, find。
     * 运行环境:在windows平台只能执行部分功能,而在Liunx环境能执行该程序的所有功能。
     * 文件功能:程序入口,main函数的实现,控制程序总流程。
     * 文件依赖:依赖Controler.h文件,主要初始化Controler类对象,依赖Controler对象执行流程。
     * 程序执行流程:先为用户显示出欢迎菜单,然后让用户输入命令,通过Controler类对象内部分析用户指令。
     *                循环输入和分析过程,直到用户输入"exit"退出程序指令。
     * 作者名:Neicole
     * 制作日期:2012.12.15
     * 联系方式:http://blog.csdn.net/neicole
     **/
    
    #include "Controler.h"
    #include <stdio.h>
    
    #define _FUNCTION_IN_UNIX_
    
    int main()
    {
        void welcome();
        void exitAbout();
        welcome();
        Controler * control = new Controler();      // 从堆中为control对象申请内存空间
        int breakWhile = 1;
        do{
            if(control -> getTrueCommand()){
                breakWhile = control -> startCommand();
            }
        }while(0 != breakWhile);
        delete control;         // 释放对象所占用的内存空间
        exitAbout();
        return 0;
    }
    
    /**
     * 函数名称:void welcome();
     * 函数功能:显示欢迎菜单,函数内含宏命令,
     *            当是Linux环境时,调用Linux环境的菜单。否则调用Windows环境欢迎菜单。
     * 函数参数:函数没有入口参数和返回值。
     **/
    void welcome()
    {
        #ifdef _FUNCTION_IN_UNIX_
        printf("\033[1m\033[33m\n");
        printf("@--------------> \033[31m欢迎来到Linux环境之简单命令解释器 \033[33m<-----------@\n");
        printf("@                                                              @\n");
        printf("@ \033[32mdate                                            显示当前日期 \033[33m@\n");
        printf("@ \033[32mpwd                                 显示当前所在目录的路径名 \033[33m@\n");
        printf("@ \033[32mdir     <目录名>            列出指定目录名中的所有目录及文件 \033[33m@\n");
        printf("@ \033[32mcd      <目录名或路径>                      改变当前工作目录 \033[33m@\n");
        printf("@ \033[32mnewdir  <目录名1>...<目录名n>                       新建目录 \033[33m@\n");
        printf("@ \033[32mdeldir  <目录名1>...<目录名n>                       删除目录 \033[33m@\n");
        printf("@ \033[32mrename  <旧文件名> <新文件名>           重命名一个文件或目录 \033[33m@\n");
        printf("@ \033[32mfind    <目录> -name <文件名>     在目录及其子目录中查找文件 \033[33m@\n");
        printf("@ \033[32mexit                                        退出命令解释程序 \033[33m@\n");
        printf("\n\033[0m");
        #else
        printf("\n");
        printf("@--------------> 欢迎来到Linux环境之简单命令解释器 <-----------@\n");
        printf("@                                                              @\n");
        printf("@ date                                            显示当前日期 @\n");
        printf("@ pwd                                 显示当前所在目录的路径名 @\n");
        printf("@ dir     <目录名>            列出指定目录名中的所有目录及文件 @\n");
        printf("@ cd      <目录名或路径>                      改变当前工作目录 @\n");
        printf("@ newdir  <目录名1>...<目录名n>                       新建目录 @\n");
        printf("@ deldir  <目录名1>...<目录名n>                       删除目录 @\n");
        printf("@ rename  <旧文件名> <新文件名>           重命名一个文件或目录 @\n");
        printf("@ find    <目录> -name <文件名>     在目录及其子目录中查找文件 @\n");
        printf("@ exit                                        退出命令解释程序 @\n");
        printf("\n");
        #endif
    }
    
    
    /**
     * 函数名称:voidexitAbout();
     * 函数功能:显示退出菜单,函数内含宏命令,
     *            当是Linux环境时,调用Linux环境的菜单。否则调用Windows环境退出菜单。
     * 函数参数:函数没有入口参数和返回值。
     **/
     void exitAbout()
     {
        #ifdef _FUNCTION_IN_UNIX_
        printf("\033[1m\n");
        printf("\033[1m\033[36m( ^_^ )/~~拜拜   \033[0m谢谢使用本程序!\n");
        printf("                 \033[0m软件制作者: Neicole\n");
        printf("                 \033[0m软件制作日期:2012年12月13日\n");
        printf("                 \033[0m联系方式:    http://blog.csdn.net/neicole\n");
        printf("\n\033[0m");
        #else
        printf("\n");
        printf("( ^_^ )/~~拜拜   谢谢使用本程序!\n");
        printf("                 软件制作者: Neicole\n");
        printf("                 软件制作日期:2012年12月13日\n");
        printf("                 联系方式:http://blog.csdn.net/neicole\n");
        printf("\n");
        #endif
        return;
     }
    


     

    Controler.cpp

     

    // Controler.cpp
    
    #include "Controler.h"
    
    /**
     * 构造函数: Controler::Controler();
     * 函数功能:无参,函数能将本类中的所有成员变量进行初始化。
     **/
    Controler::Controler()
    {
        functionImplement = new FunctionImplement();
        commandAnalysis = new CommandAnalysis();
    }
    
    /**
     * 析构函数: Controler::~Controler();
     * 函数功能:将本类中己从堆中申请的内存空间撤销。
     **/
    Controler::~Controler()
    {
        if(NULL != functionImplement){
            delete functionImplement;
        }
        if(NULL != commandAnalysis){
            delete commandAnalysis;
        }
    }
    
    /**
     * 函数名称:bool Controler::getTrueCommand();
     * 函数功能:获取用户输入指令及规范化用户输入指令格式。
     * 前置条件:已初始化commandAnalysis私有成员变量。
     * 返回值: 返回bool值,若成功获取且规范化用户指令则返回true;
     *           否则返回false.
     * 后置条件:用户输入指令及分析结果存放于commandAnalysis私有成员变量中。
     **/
    bool Controler::getTrueCommand()
    {
        bool res;
        commandAnalysis -> getUserCommand();
        res = commandAnalysis -> formatCommand();
        return res;
    }
    
    /**
     * 函数名称:bool Controler::getTrueCommand();
     * 函数功能:启动用户所输入的指令。
     * 前置条件:已初始化commandAnalysis和functionImplement私有成员变量。
     * 返回值:  -1:NoCommand, 0:exit, 1:pwd, 2:dir, 3:cd, 4:newdir,
     *             5:deldir, 6:rename, 7:find, 8:date
     **/
    int Controler::startCommand()
    {
        int res = 0;
        res = functionImplement -> start(commandAnalysis -> getResultList());
        return res; // 0代表跳出输入的循环,也代表exit
    }
    


     

    FunctionImplement.cpp

     

    // FunctionImplement.cpp
    
    #include "FunctionImplement.h"
    
    #include <vector>
    #include <string>
    #include <cstdio>
    #include <unistd.h>
    #include <sys/types.h>
    #include <dirent.h>
    #include <errno.h>
    #include <dirent.h>
    #include <time.h>
    
    #define _FUNCTION_IN_UNIX_  // 如果在Linux C 环境下,则#define _FUNCTION_IN_UNIX_
    
    #ifdef _FUNCTION_IN_UNIX_
        #include <ftw.h>        // 查找功能所需头文件(在windows平台下不支持)
        #include <sys/stat.h>   // 查找功能所需头文件(在windows平台下不支持)
    #endif
    
    using std::string;
    using std::vector;
    
    /**
     * 函数名称:int start(vector<string>);
     * 函数参数:vector<string>作为字符串容器,第0位存放指令名称,其它位存放指令参数。
     * 函数功能:判断用户己格式化好的指令类型及自动进行方法调用。
     * 返回值:  -1:NoCommand, 0:exit, 1:pwd, 2:dir, 3:cd, 4:newdir,
     *             5:deldir, 6:rename, 7:find, 8:date
     **/
    int FunctionImplement::start(vector<string> comm)
    {
        if(0 == comm.size()){         // 容器为空时,无指令
            printf("NO COMMAND\n");
            return -1;
        }
    
        int res;
        if("pwd" == comm[0]){
            myPwd();
            res = 1;
        }
        else if("dir" == comm[0]){
            myDir(comm[1]);
            res = 2;
        }
        else if("cd" == comm[0]){
            myCd(comm[1]);
            res = 3;
        }
        else if("newdir" == comm[0]){
            for(int i = 1; i < (int)comm.size(); ++i){   // n个参数同一指令的执行。
                myNewdir(comm[i]);
            }
            res = 4;
        }
        else if("deldir" == comm[0]){
            for(int i = 1; i < (int)comm.size(); ++i){// n个参数同一指令的执行。
                myDeldir(comm[i]);
            }
            res = 5;
        }
        else if("exit" == comm[0]){
            myExit();
            res = 0;
        }
        else if("rename" == comm[0]){ // n个参数同一指令的执行。
            for(int i = 1; i < (int)comm.size()-1; i = i + 2){    // 减1是因为接受双参
                myRename(comm[i],  comm[i+1]);
            }
            res = 6;
        }
        else if("find" == comm[0]){
            myFind(comm[1],  comm[2]);
            res = 7;
        }
        else if("date" == comm[0]){
            myDate();
            res = 8;
        }
        else{
            printf("BAD COMMAND\n");
            res = -1;
        }
        return res;
    }
    
    // 构造函数(无需初始化任何参数)
    FunctionImplement::FunctionImplement()
    {
    }
    
    // 析构函数(若类静态指针指向本对象,则将其置空)
    FunctionImplement::~FunctionImplement()
    {
        if(this == myObject){   // 与this指针相同时,证明是该对象用过该静态类指针
            myObject = NULL;
        }
    }
    
    /**
     * 函数名称:void FunctionImplement::myPwd();
     * 函数功能:显示当前所在目录的路径名,无法显示则给出错误提示。
     * 注意事项:函数只支持目录地址在500字符内的路径获取。
     **/
    void FunctionImplement::myPwd()
    {
        char buf1[500];
        if(NULL != getcwd(buf1, 500)){
            #ifdef _FUNCTION_IN_UNIX_
             printf("\033[31m当前工作目录:\033[0m%s\n", buf1);
            #else
             printf("当前工作目录:%s\n", buf1);
            #endif
        }
        else{
             printf("错误:当前路径过长或用户权限不足,错误代号:%d\n", errno);
        }
        return;
    }
    
    
    /**
     * 函数名称:void FunctionImplement::myDir(string addr);
     * 函数功能:列出指定目录名中的所有目录及文件。(通过readdir()函数实现功能)
     * 依赖函数:void FunctionImplement::myPwd();
     **/
    void FunctionImplement::myDir(string addr)
    {
        if("" == addr){
            char buf1[500];
            if(NULL == getcwd(buf1, 500)){
                printf("错误:无法使用当前路径进行操作,当前路径过长或用户权限不足,错误代号:%d\n", errno);
                return;
            }
            addr = buf1;
        }
        // 打开目录
        DIR * dir = opendir(addr.c_str());
        if(NULL == dir){    // 打开目录失败
            printf("打开且陈列目录错误:");
            switch(errno){
                case EMFILE: printf("已达到进程可同时打开的文件数上限。\n");break;
                case ENFILE: printf("已达到系统可同时打开的文件数上限。\n");break;
                case ENOTDIR: printf("参数name非真正的目录。\n");break;
                case ENOENT: printf("参数name 指定的目录不存在,或是参数name 为一空字符串。\n");break;
                case ENOMEM: printf("核心内存不足。\n");break;
                default: printf("错误代码:%d \n", errno);break;
            }
            return;
        }
    
        // 遍历目录
        #ifdef _FUNCTION_IN_UNIX_
            printf("\033[31m[遍历目录]\033[34m%s\033[0m\n", addr.c_str());
        #else
            printf("[遍历目录]%s\n", addr.c_str());
        #endif
        int i = 1;
        struct dirent * ptr;
        for(; NULL != (ptr = readdir(dir)); ++i)    // 读取下一个节点
        {
           if(0 == strcmp("..", ptr -> d_name) || 0 == strcmp(".", ptr -> d_name)){       // 去掉几个点点
                --i;
                continue;
            }
            #ifdef _FUNCTION_IN_UNIX_
                printf("\033[31m[名称]\033[0m%-22s", ptr -> d_name);  // 在Linux中字体带颜色
                if(0 == i%2){
                    printf("\n");
                }
            #else
                printf("名称:%s\n", ptr -> d_name);
            #endif
        }
        if(1 == i){
            printf("提示信息:这是一个空目录。\n");
        }
        printf("\n");
    
        // 关闭目录
        closedir(dir);
        return;
    }
    
    /**
     * 函数名称:void FunctionImplement::myCd(string addr);
     * 函数功能:改变当前工作目录
     **/
    void FunctionImplement::myCd(string addr)
    {
        int res = chdir(addr.c_str());
        if(0 != res){
            printf("提示信息:目录更改失败,请输入正确的工作目录!\n");
        }
        return;
    }
    
    /**
     * 函数名称:void FunctionImplement::myNewdir(string);
     * 函数功能:新建文件目录。
     **/
    void FunctionImplement::myNewdir(string addr)
    {
        #ifdef _FUNCTION_IN_UNIX_
            int res = mkdir(addr.c_str(), S_IRWXU);
            if(0 != res){
                printf("提示信息:创建%s目录失败,", addr.c_str());
                switch(errno){
                    case EACCES: printf("不具有写入操作的权限。\n");break;
                    case EEXIST: printf("目录己存在。\n");break;
                    case EMLINK: printf("父目录文件数目过多。\n");break;
                    case ENOSPC: printf("磁盘没有足够的空间写入。\n");break;
                    case EROFS: printf("不可对只读文件系统写入。\n");break;
                    default: printf("名字有冲突或发生异常。\n");break;
                }
            }
        #else
            int res = _mkdir(addr.c_str());
            if(0 != res){
                printf("提示信息:目录创建失败,请输入正确的工作目录!\n");
            }
        #endif
        return;
    }
    
    /**
     * 函数名称:void FunctionImplement::myDeldir(string addr);
     * 函数功能:删除文件目录(注意:目录必须为空).
     **/
    void FunctionImplement::myDeldir(string addr)
    {
     //   addr="test";          // 测试目录
        int res = 0;
        #ifdef _FUNCTION_IN_UNIX_
            res = rmdir(addr.c_str());
        #else
            res = _rmdir(addr.c_str());
        #endif
            if(0 != res){
                printf("提示信息:目录删除失败,请输入正确的空目录!\n");
            }
    
        return;
    }
    
    /**
     * 函数名称:void FunctionImplement::myRename(string oldName,  string newName);
     * 函数功能:重命名一个文件或目录。
     **/
    void FunctionImplement::myRename(string oldName,  string newName)
    {
        if(0 != rename(oldName.c_str(),newName.c_str())){
            printf("提示信息:重命名失败,请输入正确文件名!错误代号:%d 。\n",  errno);
        }
        return;
    }
    
    // 用于查找功能的静态指针的初始化
    FunctionImplement * FunctionImplement::myObject = NULL;
    //在指定的目录及其子目录中查找指定的文件(辅助函数)
    int myFindAssistant(const char *file, const struct stat* sb, int flag)
    {
        #ifdef _FUNCTION_IN_UNIX_
            if(flag == FTW_F)
            {
               FunctionImplement::myObject -> myFindAssistant2(file);
            }
        #endif
        return 0;
    }
    
    /**
     * 函数名称:int FunctionImplement::myFindAssistant2(const char *);
     * 函数功能:查找功能的最终实现函数,
     *           (实际上这函数是对单个文件的判断,判断传入参数路径是否需寻找的目标文件)
     * 函数实现:先分析路径,如是根路径,则直接与targetName进行对比,
     *            如非根路径,则将文件名提取出来后与targetName对比。
     **/
    int FunctionImplement::myFindAssistant2(const char * finding)
    {
        #ifdef _FUNCTION_IN_UNIX_
            string fileName = targetName;
            string findingStr(finding);
     //      printf("finding: %s\n target:%s\n", finding, targetName.c_str());
    
            size_t index = findingStr.find_last_of("/");
            if(string::npos != index){          // 找到\, 如果不在根目录,先去除路径名
                findingStr.erase(0, index+1);
     //           printf("计算后的findingStr为:%s\n", findingStr.c_str());
            }
            if ( 0 == strcmp(findingStr.c_str(), targetName.c_str())){// 匹配,则输出结果
                printf("%s\n", finding);
                ++targetNum;
            }
        #endif
        return 0;
    }
    
    /**
     * 函数名称:void FunctionImplement::myFind(string dirStr, string fileName);
     * 函数依赖:this -> getcwd(char*); ::myFindAssistant(...);
     * 函数功能:作为查找功能的入口函数,在指定的目录及其子目录中查找指定的文件。
     **/
    void FunctionImplement::myFind(string dirStr, string fileName)
    {
        #ifdef _FUNCTION_IN_UNIX_
            // 如果没有第一个参数,则默认获取当前目录,把当前目录作为查找操作的根目录。
            if("" == dirStr){
                char buf1[500];
                if(NULL == getcwd(buf1, 500)){
                    printf("错误:无法使用当前路径进行操作,当前路径过长或用户权限不足,错误代号:%d\n", errno);
                    return;
                }
                dirStr = buf1;
            }
            // 初始化待会函数需要用上的静态指针
            myObject = this;
            // 将需要查找的文件名通过成员变量传出,由后面程序交由this->myFindAssistant2(const char * )处理。
            targetName = fileName;
            // 统计找到的文件数。
            targetNum = 0;
            // 开始调用递归目录寻找文件的函数
            ftw(dirStr.c_str(), myFindAssistant, 500);
            // 对文件数目进行处理
            if(0 == targetNum){
                printf("提示:目录不存在或没有在指定目录中找到文件。\n");
            }
        #endif
        return;
    }
    
    /**
     * 函数名称:void FunctionImplement::myDate();
     * 函数功能:显示当前日期。
     **/
    void FunctionImplement::myDate()
    {
        time_t timep;
        struct tm *p;
        time(&timep);          // 返回从公元1970年1月1日的UTC时间从0时0分0秒算起到现在所经过的秒数。
        p = localtime(&timep); // 取得当地时间(转换时间为标准格式)
        printf ("%d Year %d Month %d Day\n", (1900 + p->tm_year),(1 + p->tm_mon), p -> tm_mday);
    //    printf(“%s%d:%d:%d\n”, wday[p->tm_wday],p->tm_hour, p->tm_min, p->tm_sec);
        return;
    }
    
    
    //退出命令解释程序
    void FunctionImplement::myExit()
    {
        return;// doNothing 交给程序流程控制去做,在main函数中 return 0;
    }
    


     

    CommandAnalysis.cpp

    // CommandAnalysis.cpp
    #include "CommandAnalysis.h"
    #include <iostream>
    
    using std::cout;
    using std::cin;
    using std::endl;
    
    /**
     * 函数名称:void CommandAnalysis::constructComm();
     * 函数功能:初始化set集,将需要用上的指令放入对应的指令类型集中。
     **/
    void CommandAnalysis::constructComm()
    {
        comm0p.insert("pwd");
        comm0p.insert("exit");
        comm0p.insert("date");
        comm1p.insert("cd");
        comm1np.insert("newdir");
        comm1np.insert("deldir");
        comm1p_0p.insert("dir");
        comm2pn.insert("rename");
        comm1p1o1p_1o1p.insert("find");
        optionComm.insert("-name");
        return;
    }
    
    /**
     * 构造函数: CommandAnalysis::CommandAnalysis();
     * 函数功能:初始化时,通过调用本类函数初始化指令集。
     **/
    CommandAnalysis::CommandAnalysis()
    {
        this -> constructComm();
    }
    
    // 析构函数
    CommandAnalysis::~CommandAnalysis()
    {
        // 里面没有野指针,无需构造
    }
    
    
    /**
     * 函数名称:void CommandAnalysis::saveAndClearCurString()
     * 函数功能:将私有成员currentStr存入私有成员resultList中,存入后将currentStr内容清空。
     **/
    void CommandAnalysis::saveAndClearCurString()
    {
        resultList.push_back(currentStr);
        currentStr.clear();
        return;
    }
    
    /**
     * 函数名称:void CommandAnalysis::startPrepare();
     * 函数功能:为开始获取命令,分析命令做准备,
     *     清空所有与用户指令及指令结果有关的字符串信息。
     **/
    void CommandAnalysis::startPrepare()
    {
        userCommand.clear();
        resultList.clear();
        currentStr.clear();
        currentIndex = -1;  // 设为-1与下面的获取单词的算法有关
    }
    
    /**
     * 函数名称:bool CommandAnalysis::getNextWord();
     * 前置条件:currentIndex设为需要获取单词的首字母前一位。
     * 函数功能:从用户输入的指令串中获取下一单词存放于currentStr变量中。
     * 后置条件;将新获取的单词存于currentStr变量中。
     * 返回值:true:成功获取出由' ','"'或‘\0'分隔的单词。
     *          false:不能获取单词,currentIndex越界或双引号中内容为空。
     **/
    bool CommandAnalysis::getNextWord()
    {
        currentStr.clear();
        // 将下标移到下一位
        if((long)userCommand.length() <= ++currentIndex){
            return false;
        }
    
        // 去除空格(实际上是下标移动)
        while(' ' == userCommand[currentIndex] || '\t' == userCommand[currentIndex]){
            if((long)userCommand.length() <= ++currentIndex){
                return false;
            }
        }
    
        // 带空格目录时有可能出现双引号,此时不能以空格判断结束,直到下一个双引号或终结位置才结束
        // 不储存双引号
        if('\"' == userCommand[currentIndex]){
            if((long)userCommand.length() <= ++currentIndex){ // 下标移动,直到字符串末尾
                    return false; // 只有一个单引号,返回错误
            }
            if(' ' == userCommand[currentIndex] && '\t' == userCommand[currentIndex] && '\0' == userCommand[currentIndex]){
                return false;   // 空串
            }
            while(true){
                currentStr.push_back(userCommand[currentIndex]);
                if((long)userCommand.length() <= ++currentIndex){ // 下标移动,直到字符串末尾
                    return true;
                }
                if('\"' == userCommand[currentIndex]){      // 遇到第二个双引号则存储后跳出
                    currentStr.push_back(userCommand[currentIndex]);
                    return true;
                }
            }
        }
    
        // 空格或字符串结束符作为结束标记
        while(' ' != userCommand[currentIndex] && '\t' != userCommand[currentIndex] && '\0' != userCommand[currentIndex]){
            currentStr.push_back(userCommand[currentIndex]);
            if((long)userCommand.length() <= ++currentIndex){ // 下标移动,直到字符串末尾
                return true;
            }
        }
    
        return true;
    }
    
    /**
     * 函数名称:bool  CommandAnalysis::judgeComm0p();
     * 函数功能:规范化无参命令,将规范结果存放于vector<string>resultList中。
     * 容错性:在规范化时,若接收了无参命令,则忽略后面的参数。
     **/
    bool  CommandAnalysis::judgeComm0p()
    {
        this -> saveAndClearCurString();        // 保存命令
        /* 注释掉以增强容错性
        if(true == getNextWord()){      // 不符合无参命令的规则
            cout << resultList[0] << "命令不带参数,请以正确格式输入命令!\n";
            return false;
        }
        */
        return true;
    }
    
    /**
     * 函数名称:bool CommandAnalysis::judgeComm1p();
     * 函数功能:规范化带1位参数的命令,将规范结果存放于vector<string>resultList中。
     * 容错性:在规范化时,若为接收了正确的带1参命令及参数,则忽略后面的参数。
     **/
    bool CommandAnalysis::judgeComm1p()
    {
        this -> saveAndClearCurString();        // 保存命令
        if(true == getNextWord()){              // 获取第1个参数
            this -> saveAndClearCurString();
        }
        else{
            cout << resultList[0] << "命令至少需要一个参数,请以正确格式输入命令!\n";
            return false;
        }
        /* 注释掉以增强容错性
        if(true == getNextWord()){      // 不符合1参命令的规则
            cout << resultList[0] << "命令参数过多,请以正确格式输入命令!\n";
            return false;
        }
        */
        return true;
    }
    
    
    /**
     * 函数名称:bool CommandAnalysis::judgeComm1np();
     * 函数功能:规范化带1位或多位参数的命令,将规范结果存放于vector<string>resultList中。
     * 特性:在规范化时,若为接收了正确的带1参命令及参数,则可继续接收后面的参数。
     **/
    bool  CommandAnalysis::judgeComm1np()
    {
        this -> saveAndClearCurString();        // 保存命令
        if(true == getNextWord()){              // 获取第1个参数
            this -> saveAndClearCurString();
        }
        else{
            cout << resultList[0] << "命令至少需要一个参数,请以正确格式输入命令!\n";
            return false;
        }
        while(true == getNextWord()){           // 获取第n个参数
            this -> saveAndClearCurString();
        }
        return true;
    }
    
    /**
     * 函数名称:bool CommandAnalysis::judgeComm1p_0p();
     * 函数功能:规范化带1位或1位默认参数参数的命令,将规范结果存放于vector<string>resultList中。
     * 特性:在规范化时,若接收的命令为带1参命令,但不能获取到参数,则将默认参数设为""空串存放。
     **/
    bool CommandAnalysis::judgeComm1p_0p()
    {
        this -> saveAndClearCurString();        // 保存命令
        if(true == getNextWord()){              // 获取第1个参数
            this -> saveAndClearCurString();
        }
        else{
            currentStr = "";                    //  加上默认参数
            this -> saveAndClearCurString();
        }
        /* 注释掉以增强容错性
        if(true == getNextWord()){      // 不符合2参命令的规则
            cout << resultList[0] << "命令参数过多,请以正确格式输入命令!\n";
            return false;
        }
        */
        return true;
    }
    
    /**
     * 函数名称:bool CommandAnalysis::judgeComm2pn()
     * 函数功能:规范化带2位或多位参数的命令,将规范结果存放于vector<string>resultList中。
     * 特性:在规范化时,若为接收了正确的带2参命令及参数,则可继续接收后面的参数。
     *        在接收后面的参数时,只进行两位同时接收,如果为单数,则忽略最后的参数。
     **/
    bool CommandAnalysis::judgeComm2pn()
    {
        this -> saveAndClearCurString();        // 保存命令
        if(true == getNextWord()){  // 获取两个参数
            this -> saveAndClearCurString();
        }
        else{
            cout << resultList[0] << "命令至少需要两个参数,请以正确格式输入命令!\n";
            return false;
        }
        if(true == getNextWord()){  // 获取两个参数
            this -> saveAndClearCurString();
        }
        else{
            cout << resultList[0] << "命令至少需要两个参数,请以正确格式输入命令!\n";
            return false;
        }
        /* 注释掉以增强容错性
        if(true == getNextWord()){      // 不符合两参命令的规则
            cout << resultList[0] << "命令参数过多,请以正确格式输入命令!\n";
            return false;
        }
        */
        while(true == getNextWord()){// 开始扩展重命令功能,给多个文件重命名,只会双个双个参数接受
            this -> saveAndClearCurString();
            if(true == getNextWord()){
              this -> saveAndClearCurString();
            }
            else{       // 不能连续获取第二个参数则从结果容器中弹出
              resultList.pop_back();
            }
        }
        return true;
    }
    
    /**
     * 函数名称:bool CommandAnalysis::judgeComm1p_0p();
     * 函数功能:规范化[command x -y z]形式的命令,将规范结果存放于vector<string>resultList中。
     * 特性:在规范化时,若[command x -y z]将x省略则将自动将x设为""空串继续以规范化格式存放。
     **/
    bool  CommandAnalysis::judgeComm1p1o1p_1o1p()
    {
        this -> saveAndClearCurString();        // 保存命令
        if(true == getNextWord()){
             if(optionComm.count(currentStr)){   //  条件成立则带1默认参数
                  currentStr = "";            //  加上默认参数
                  this -> saveAndClearCurString();
                  if(true == getNextWord()){      // 获取最后一位参数
                      this -> saveAndClearCurString();
                  }
                  else{
                       cout << resultList[0] << "命令参数错误,请以正确格式输入命令!\n";
                       return false;
                  }
                  /* 注释掉以增强容错性
                   if(true == getNextWord()){      // 不符合两参命令的规则
                     cout << resultList[0] << "命令参数过多,请以正确格式输入命令!\n";
                     return false;
                   }
                   */
              }
              else{  // 无默认参数的情况
                    this -> saveAndClearCurString();   // 存入第一参数
                    if(true == getNextWord() && optionComm.count(currentStr)){
                        if(true == getNextWord()){      // 获取最后一位参数
                           this -> saveAndClearCurString();
                        }
                        else{
                            cout << resultList[0] << "命令参数错误,请以正确格式输入命令!\n";
                            return false;
                        }
                    }
                    else{
                        cout << resultList[0] << "命令参数错误,请以正确格式输入命令!\n";
                        return false;
                    }
                }
                /* 注释掉以增强容错性
               if(true == getNextWord()){      // 不符合两参命令的规则
                 cout << resultList[0] << "命令参数过多,请以正确格式输入命令!\n";
                 return false;
               }
               */
            }
           else{
              cout << resultList[0] << "命令参数错误,请以正确格式输入命令!\n";
              return false;
            }
            return true;
    }
    
    /**
     * 函数名称:void CommandAnalysis::getUserCommand();
     * 函数功能:获取用户输入指令。
     **/
    void CommandAnalysis::getUserCommand()
    {
        this -> startPrepare();
     //   cout << "jiangzhenqian@ ";   // Windows平台样式
        cout << "\033[1mjiangzhenqian@ \033[0m";        // Linux平台样式
        // 获取一行字符存放于userCommand中
        std::cin.sync();
        for( char ch = getchar(); '\n' != ch; ch = getchar()){
            userCommand.push_back(ch);
        }
        return;
    }
    
    /**
     * 函数名称:bool CommandAnalysis::formatCommand();
     * 前置条件:vector<string>resultList成员变量为空。
     * 函数功能:将命令规范化。
     * 后置条件:成功规范化的指令以字符串序列的方式存放于vector<string>resultList变量中。
     **/
    bool CommandAnalysis::formatCommand()
    {
        bool res = true;
        if(true == getNextWord()){
    
            if(comm0p.count(currentStr)){       // 属于无参命令
                res =  this -> judgeComm0p();
            }
            else if(comm1p.count(currentStr)){  // 属于带1参命令
                res =  this -> judgeComm1p();
            }
            else if(comm1np.count(currentStr)){  // 属于带1个或以上参数的命令
                res =  this -> judgeComm1np();
            }
            else if(comm1p_0p.count(currentStr)){// 属于带1参或无参命令
                res =  this -> judgeComm1p_0p();
            }
            else if(comm2pn.count(currentStr)){// 属于带2参命令
                res = this -> judgeComm2pn();
            }
            else if(comm1p1o1p_1o1p.count(currentStr)){// 属于带1p1o1p或1o1p命令
                res = this -> judgeComm1p1o1p_1o1p();
            }
            else{// 不属于支持的命令
                 cout << "不支持该命令,请输入正确的命令!\n";
                 res = false;
            }
        }
        else{
            cout << "未发现指令,请输入指令。\n";
            res = false;
        }
        return res;
    }
    
    /**
     * 函数名称:vector<string> CommandAnalysis::getResultList();
     * 函数功能:获取分析结果变量。
     **/
    vector<string> CommandAnalysis::getResultList()
    {
        return resultList;
    }
    


     

    展开全文
  • 基于DOS系统,一个简单的命名解析器,利用DOS中断中21H中断,实现了列目录、时间显示、日期显示、显示字符串等功能。
  • Linux shell命令解析器(一),bash终端

    千次阅读 2018-11-04 13:03:50
    环境: Ubuntu14-4 内核 4.4.0-135 vim编辑 7.4  ...1.命令解释首先是一个死循环。 2.打印一个命令提示符。 3.取得命令行输入放在数组里面,要求命令带参数。可以getc()、fgets()、scanf()等。...
  • systemctl命令解析

    千次阅读 2018-10-16 20:04:12
    原文链接如果有效,请点击原文链接查看。 ... 一、由来 历史上,Linux 的启动一直采用init进程。 下面的命令用来启动服务。 $ sudo /etc/init.d/apache2 start # 或者 $ service apache2 st...
  • 在计算机科学中,Shell俗称壳(用来区别于核),是指“提供使用者使用界面”的软件(命令解析器)。它类似于DOS下的command.com。它接收用户命令,然后调用相应的应用程序。同时它又是一种程序设计语言。作为命令...
  • Crontab命令解析

    万次阅读 2017-03-06 11:23:42
    分 时 日 月 周 命令  第1列表示分钟1~59 每分钟用*或者 */1表示  第2列表示小时1~23(0表示0点)  第3列表示日期1~31  第4列表示月份1~12  第5列标识号星期0~6(0表示星期天)  第6列要运行...
  • 说说终端的命令解析器shell 和 命令执行的过程....
  • Linux:objdump命令解析

    万次阅读 多人点赞 2018-09-03 16:05:43
    objdump命令是Linux下的反汇编目标文件或者可执行文件的命令,它以一种可阅读的格式让你更多地了解二进制文件可能带有的附加信息。 参数选项: --archive-headers -a 显示档案库的成员信息,类似ls -l将lib*....
  • 一个类似与DOS界面的命令解析器,用C语言编写的,操作系统的作业,很简单的一个程序。
  • shell脚本【命令解析过程】

    千次阅读 2018-06-16 10:24:55
    必须要知道命令解析过程的意义在于:能够清楚的知道命令解析步骤。若发生错误时,能够知道该怎样更改。 比如在博客:I/O重定向(点击打开链接)的举例:例①中,就有因为不知道命令行是如何读取的,而造成while语句...
  • virsh的详细命令解析

    万次阅读 2015-11-12 17:35:58
    virsh的详细命令解析virsh 有命令模式和交互模式如果直接在vrish后面添加参数是命令模式,如果直接写virsh,就会进入交互模式 virsh list 列出所有的虚拟机,虚拟机的状态有(8)种 runing 是运行状态 idel 是...
  • PostgreSQL命令解析

    千次阅读 2008-08-05 13:32:00
    psqlNamepsql -- PostgreSQL 交互终端 Synopsispsql [option...] [dbname [username]]描述psql 是一个以终端为基础的 PostgreSQL 前端。...还有, 它提供了一些元命令和多种类 shell 地特性来实现书写脚本
  • NTP相关命令解析

    万次阅读 2018-01-12 16:35:20
    通过hwclock --show 命令我们可以查看机器上的硬件时间(always in local time zone), 我们可以看到它和系统时间还是有一定的误差的, 那么我们就需要把他们同步 如果我们想要把硬件时间设置成系统时间我们可以...
  • Linux命令:使用dig命令解析域名

    万次阅读 2013-10-23 19:13:34
    Linux下解析域名除了使用nslookup之外,开可以使用dig命令解析域名,dig命令可以得到更多的域名信息。 dig的全称是 (domain information groper)。它是一个用来灵活探测DNS的工具。它会打印出DNS name server的...
  • 单片机串口实现字符串命令解析

    千次阅读 2020-10-30 16:20:38
    下面就贴出完整的代码 命令解析头文件 cmd.h : #ifndef __CMD_H_ #define __CMD_H_ #include "iostm8s103F3.h" #define ARRAY_SIZE(x) (sizeof(x) / (sizeof((x)[0]))) //用来计算结构体数组中,数组的个数。...
  • Pycharm-设置解析器+下载解析器

    千次阅读 2019-08-16 22:21:42
    pycharm 解析器的配置、添加镜像 便于下载的内容
  • CCP协议学习-命令解析

    千次阅读 2018-11-17 12:00:24
    在本章中,将解释所有带有参数的命令和预期的返回信息,包括每个命令的示例。 12.1 连接 指令标签CONNECT CRO中的数据结构 位置 类型 描述 0 byte ...
  • 在下载和安装XML解析器的过程中可能用到的一些linux下的命令: //查看linux处理器构架是多少位: $ cat /proc/version (有 x86_64 说明是64位 ) //查看目前你所用的shell是什么版本:(有如下的三种...
  • 介绍 Linux系统扫描SCSI磁盘有几种方式?Linux新增LUN之后,能否不重启主机就...Linux系统提供多重机制以重新扫描SCSI总线并重认系统中加入的SCSI设备。在2.4内核方案中,由于动态LUN扫描机制不具备一致性,往往需要
  • linux命令解析json字符串

    千次阅读 2020-02-18 19:15:19
    1、centos安装jq命令 一、简介 EPEL是企业版Linux附加软件包的简称,EPEL是一个由Fedora特别兴趣小组创建、维护并管理的,针对 红帽企业版linux(RHEL)及其衍生发行版(比如CentOS、Scientific Linux、Oracle...
  • 计算机系统 自带实用命令+暗黑工具 大全集【建议收藏】
  • Linux系统常用的基本命令

    万次阅读 多人点赞 2018-08-15 15:11:08
    本文主要介绍Linux中常用的基本命令,简单实用,以供大家参考,如有错误,欢迎大家指出。下面开始介绍。 一、查看哪个用户登录的系统 1、users命令 2、whoami命令或者who am i命令 二、查看哪些用户在系统上...
  • 要将位于远程主机上的文件系统挂载到本地挂载点上,可以使用: sshfd user@remotehost:/home/path /mnt/mountpoint 然后输入密码即可 现在位于远程主机/home/path中的数据就可以通过本地挂载点/mnt/mountpoint来...
  • MLT框架简介与部分命令解析

    千次阅读 2019-04-10 14:17:15
    系统的功能是通过提供各式各样的工具集、XML编辑组件和一组基于可扩展插件的API来实现。“  官方的解释可能不是太通俗易懂,大概意思就是这个框架可以用来对视频/音频做剪辑、合并、增加一些滤镜特效等,其提供...
  • GPRS 拨号,at命令解析

    千次阅读 2009-10-29 13:50:00
    使用AT命令调试调制解调 -------------------------------------------------------------------------------- 发布时间:2006-1-6 17:43:52 使用AT命令调试调制解调 最早生产调制解调的公司是贺氏,后来组建的...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 250,255
精华内容 100,102
关键字:

系统命令解析器