精华内容
下载资源
问答
  • 目录argparse介绍argparse使用——代码示例1、创建一个解析器——创建 ArgumentParser() 对象描述description2、添加参数——调用 add_argument() 方法添加参数add_argument() 方法定义如何解析命令行参数3、解析...

    一、argparse介绍

    官方文档
    argparse 模块是 Python 内置的一个用于命令项选项与参数解析的模块,argparse 模块可以让人轻松编写用户友好的命令行接口。通过在程序中定义好我们需要的参数,然后 argparse 将会从 sys.argv 解析出这些参数。argparse 模块还会自动生成帮助和使用手册,并在用户给程序传入无效参数时报出错误信息。

    二、argparse使用——代码示例

    一个简单的示例。

    import argparse
    
    parser = argparse.ArgumentParser(description='test')
    
    parser.add_argument('--sparse', action='store_true', default=False, help='GAT with sparse version or not.')
    parser.add_argument('--seed', type=int, default=72, help='Random seed.')
    parser.add_argument('--epochs', type=int, default=10000, help='Number of epochs to train.')
    
    args = parser.parse_args()
    print(args.sparse)
    print(args.seed)
    print(args.epochs)
    

    三个步骤:

    • 1、创建一个解析器——创建 ArgumentParser() 对象
    • 2、添加参数——调用 add_argument() 方法添加参数
    • 3、解析参数——使用 parse_args() 解析添加的参数

    1、创建一个解析器——创建 ArgumentParser() 对象

    使用 argparse 的第一步是创建一个 ArgumentParser 对象:

    parser = argparse.ArgumentParser(description='test')
    

    ArgumentParser 对象包含将命令行解析成 Python 数据类型所需的全部信息。

    描述description

    大多数对 ArgumentParser 构造方法的调用都会使用 description= 关键字参数。这个参数简要描述这个程度做什么以及怎么做。在帮助消息中,这个描述会显示在命令行用法字符串和各种参数的帮助消息之间。

    2、添加参数——调用 add_argument() 方法添加参数

    给一个 ArgumentParser 添加程序参数信息是通过调用 add_argument() 方法完成的。通常,这些调用指定 ArgumentParser 如何获取命令行字符串并将其转换为对象。这些信息在 parse_args() 调用时被存储和使用。例如

    parser.add_argument('--sparse', action='store_true', default=False, help='GAT with sparse version or not.')
    parser.add_argument('--seed', type=int, default=72, help='Random seed.')
    parser.add_argument('--epochs', type=int, default=10000, help='Number of epochs to train.')
    

    add_argument() 方法定义如何解析命令行参数

    ArgumentParser.add_argument(name or flags...[, action][, nargs][, const][, default][, type][, choices][, required][, help][, metavar][, dest])

    每个参数解释如下:
    name or flags - 选项字符串的名字或者列表,例如 foo 或者 -f, --foo。
    action - 命令行遇到参数时的动作,默认值是 store。
    store_const,表示赋值为const;
    append,将遇到的值存储成列表,也就是如果参数重复则会保存多个值;
    append_const,将参数规范中定义的一个值保存到一个列表;
    count,存储遇到的次数;此外,也可以继承 argparse.Action 自定义参数解析;
    nargs - 应该读取的命令行参数个数,可以是具体的数字,或者是?号,当不指定值时对于 Positional argument 使用 default,对于 Optional argument 使用 const;或者是 * 号,表示 0 或多个参数;或者是 + 号表示 1 或多个参数。
    const - action 和 nargs 所需要的常量值。
    default - 不指定参数时的默认值。
    type - 命令行参数应该被转换成的类型。
    choices - 参数可允许的值的一个容器。
    required - 可选参数是否可以省略 (仅针对可选参数)。
    help - 参数的帮助信息,当指定为 argparse.SUPPRESS 时表示不显示该参数的帮助信息.
    metavar - 在 usage 说明中的参数名称,对于必选参数默认就是参数名称,对于可选参数默认是全大写的参数名称.
    dest - 解析后的参数名称,默认情况下,对于可选参数选取最长的名称,中划线转换为下划线.

    3、解析参数——使用 parse_args() 解析添加的参数

    ArgumentParser 通过 parse_args() 方法解析参数。它将检查命令行,把每个参数转换为适当的类型然后调用相应的操作。在大多数情况下,这意味着一个简单的 Namespace 对象将从命令行解析出的属性构建:

    args = parser.parse_args()
    

    在脚本中,通常 parse_args() 会被不带参数调用,而 ArgumentParser 将自动从 sys.argv 中确定命令行参数。

    三、结果测试

    import argparse
    
    parser = argparse.ArgumentParser(description='test')
    
    parser.add_argument('--sparse', action='store_true', default=False, help='GAT with sparse version or not.')
    parser.add_argument('--seed', type=int, default=72, help='Random seed.')
    parser.add_argument('--epochs', type=int, default=10000, help='Number of epochs to train.')
    
    args = parser.parse_args()
    
    print(args.sparse)
    print(args.seed)
    print(args.epochs)
    

    显示帮助文档:
    在这里插入图片描述
    输错命令会告诉你usage用法:
    在这里插入图片描述
    使用命令修改参数:
    在这里插入图片描述

    action='store_true’的使用说明

    action·- 命令行遇到参数时的动作,默认值是 store。

    直接运行python test.py,输出结果False
    运行python test.py --sparse,输出结果True

    也就是说,action=‘store_true’,只要运行时该变量有传参就将该变量设为True。
    在这里插入图片描述

    四、python args parse_args() 报错解决

    1、error: the following arguments are required: xxx

    报错:

    usage: test.py [-h] xxx
    test.py: error: the following arguments are required: xxx
    

    原因:

    • args分为可选参数(用--指定)和必选参数(不加--指定)。
    • 如果你定义参数xxx时,没有用--指定,那么该参数为需要在命令行内手动指定。此时即使通过default设置默认参数,也还是会报错。

    五、其他问题汇总(评论小伙伴问的)

    1、下划线_和横线-的区别

    Q1:请问博主,第一个位置参数假如说是--max_episode_len,然后也有人写是--max-episode-len,但是他在调用的时候仍然用的是args.max_episode_len,也没报错,请问这个下划线_-的区别在哪里呢?

    A1:没啥区别,在这里表示同一个意思,-对应_,代码里写的不一样或者都改成一样的都可以

    2、debug怎么进行?

    Q2:这种运行是不是脱离ide,那debug怎么进行啊?

    A2:有linux下,命令行单点调试的方法,python -m pdb test.py,搜一搜,网上挺多

    展开全文
  • Linux系统调用解析

    千次阅读 2013-01-23 19:57:37
    用户可以通过系统调用命令在自己的应用程序中调用它们。从某种角度来看,系统调用和普通的函数调用非常相似。区别仅仅在于,系统调用由操作系统核心提供,运行于核心态;而普通的函数调用由函数库或用户自己提供,...
    • 什么是系统调用?

           Linux内核中设置了一组用于实现各种系统功能的子程序,称为系统调用。用户可以通过系统调用命令在自己的应用程序中调用它们。从某种角度来看,系统调用和普通的函数调用非常相似。区别仅仅在于,系统调用由操作系统核心提供,运行于核心态;而普通的函数调用由函数库或用户自己提供,运行于用户态。   

          随Linux核心还提供了一些C语言函数库,这些库对系统调用进行了一些包装和扩展,因为这些库函数与系统调用的关系非常紧密,所以习惯上把这些函数也称为系统调用。

                     

    • 为什么要用系统调用?

         实际上,很多已经被我们习以为常的C语言标准函数,在Linux平台上的实现都是靠系统调用完成的,所以如果想对系统底层的原理作深入的了解,掌握各种系统调用是初步的要求。进一步,若想成为一名Linux下编程高手,也就是我们常说的Hacker,其标志之一也是能对各种系统调用有透彻的了解。

         即使除去上面的原因,在平常的编程中你也会发现,在很多情况下,系统调用是实现你的想法的简洁有效的途径,所以有可能的话应该尽量多掌握一些系统调用,这会对你的程序设计过程带来意想不到的帮助。

    • 系统调用是怎么工作的?

          一般的,进程是不能访问内核的。它不能访问内核所占内存空间也不能调用内核函数。CPU硬件决定了这些(这就是为什么它被称作"保护模式")。系统调用是这些规则的一个例外。其原理是进程先用适当的值填充寄存器,然后调用一个特殊的指令,这个指令会跳到一个事先定义的内核中的一个位置(当然,这个位置是用户进程可读但是不可写的)。在Intel CPU中,这个由中断0x80实现。硬件知道一旦你跳到这个位置,你就不是在限制模式下运行的用户,而是作为操作系统的内核--所以你就可以为所欲为。

           进程可以跳转到的内核位置叫做sysem_call。这个过程检查系统调用号,这个号码告诉内核进程请求哪种服务。然后,它查看系统调用表(sys_call_table)找到所调用的内核函数入口地址。接着,就调用函数,等返回后,做一些系统检查,最后返回到进程(或到其他进程,如果这个进程时间用尽)。

           具体过程如下图所示:



    • 如何使用系统调用?
    先来看一个例子:

    #include<linux/unistd.h>                /*定义宏_syscall1*/
    #include<time.h>                        /*定义类型time_t*/
    _syscall1(time_t,time,time_t *,tloc)    /*宏,展开后得到time()函数的原型*/
    main()
    {
            time_t the_time;
            the_time=time((time_t *)0);     /*调用time系统调用*/
            printf("The time is %ld\n",the_time);
    }
            系统调用time返回从格林尼治时间1970年1月1日0:00开始到现在的秒数。
            这是最标准的系统调用的形式,宏_syscall1()展开来得到一个函数原型。但事实上,如果把程序改成下面的样子,程序也可以运行得同样的结果。
    #include<time.h>
    main()
    {
            time_t the_time;
            the_time=time((time_t *)0); /*调用time系统调用*/
            printf("The time is %ld\n",the_time);
    }

           这是因为在time.h中实际上已经用库函数的形式实现了time这个系统调用,替我们省掉了调用_syscall1宏展开得到函数原型这一步。

           大多数系统调用都在各种C语言函数库中有所实现,所以在一般情况下,我们都可以像调用普通的库函数那样调用系统调用,只在极个别的情况下,我们才有机会用到_syscall*()这几个宏。


    • 调用性能问题

         系统调用需要从用户空间陷入内核空间,处理完后,又需要返回用户空间。其中除了系统调用服务例程的实际耗时外,陷入/返回过程和系统调用处理程序(查系统调用表、存储\恢复用户现场)也需要花销一些时间,这些时间加起来就是一个系统调用的响应速度。系统调用不比别的用户程序,它对性能要求很苛刻,因为它需要陷入内核执行,所以和其他内核程序一样要求代码简洁、执行迅速。幸好Linux具有令人难以置信的上下文切换速度,使得其进出内核都被优化得简洁高效;同时所有Linux系统调用处理程序和每个系统调用本身也都非常简洁。
    
         绝大多数情况下,Linux系统调用性能是可以接受的,但是对于一些对性能要求非常高的应用来说,它们虽然希望利用系统调用的服务,但却希望加快相应速度,避免陷入/返回和系统调用处理程序带来的花销,因此采用由内核直接调用系统调用服务例程,最好的例子就HTTPD——它为了避免上述开销,从内核调用socket等系统调用服务例程。

    • Linux系统调用列表

    进程控制

    fork 创建一个新进程
    clone 按指定条件创建子进程
    execve 运行 可执行文件
    exit 中止进程
    _exit 立即中止当前进程
    getdtablesize 进程所能打开的最大文件数
    getpgid 获取指定进程组标识号
    setpgid 设置指定进程组标志号
    getpgrp 获取当前进程组标识号
    setpgrp 设置当前进程组标志号
    getpid 获取进程标识号
    getppid 获取父进程标识号
    getpriority 获取调度优先级
    setpriority 设置调度优先级
    modify_ldt 读写进程的本地描述表
    nanosleep 使进程睡眠指定的时间
    nice 改变分时进程的优先级
    pause 挂起进程,等待信号
    personality 设置进程运行域
    prctl 对进程进行特定操作
    ptrace 进程跟踪
    sched_get_priority_max 取得静态优先级的上限
    sched_get_priority_min 取得静态优先级的下限
    sched_getparam 取得进程的调度参数

    sched_getscheduler 取得指定进程的调度策略
    sched_rr_get_interval 取得按RR 算法调度的实时进程的时间片长度
    sched_setparam 设置进程的调度参数
    sched_setscheduler 设置指定进程的调度策略和参数
    sched_yield 进程主动让出处理器,并将自己等候调度队列队尾
    vfork 创建一个子进程,以供执行新程序,常与execve等同时使用
    wait 等待子进程终止
    wait3 参见wait
    waitpid 等待指定子进程终止
    wait4 参见waitpid
    capget 获取进程权限
    capset 设置进程权限
    getsid 获取会晤标识号
    setsid 设置会晤标识号

    文件系统控制

    1、文件读写操作

    fcntl 文件控制
    open 打开文件
    creat 创建新文件
    close 关闭文件描述字
    read 读文件
    write 写文件
    readv 从文件读入数据到缓冲 数组
    writev 将缓冲 数组里的数据写入文件
    pread 对文件随机读
    pwrite 对文件随机写
    lseek 移动文件指针
    _ llseek 在64位 地址空间里移动 文件指针
    dup 复制已打开的文件描述字
    dup2 按指定条件复制文件描述字
    flock 文件加/解锁
    poll I/O多路转换
    truncate 截断文件
    ftruncate 参见truncate
    umask 设置文件权限掩码
    fsync 把文件在内存中的部分写回磁盘

    2、文件系统操作

    access 确定文件的可存取性
    chdir 改变当前工作目录
    fchdir 参见chdir
    chmod 改变文件方式
    fchmod 参见chmod
    chown 改变文件的属主或用户组
    fchown 参见chown
    lchown 参见chown
    chroot 改变根目录
    stat 取文件状态信息
    lstat 参见stat
    fstat 参见stat
    statfs 取文件 系统信息
    f statfs 参见statfs
    readdir 读取目录项
    getdents 读取目录项
    mkdir 创建目录
    mknod 创建索引节点
    rmdir 删除目录
    rename 文件改名
    link 创建链接
    symlink 创建符号链接
    unlink 删除链接
    readlink 读符号链接的值
    mount 安装文件系统
    umount 卸下文件系统
    ustat 取文件 系统信息
    utime 改变文件的访问修改时间
    utimes 参见utime
    quotactl 控制 磁盘配额

    系统控制

    ioctl I/O总控制 函数
    _sysctl 读/写 系统参数
    acct 启用或禁止进程记账
    getrlimit 获取 系统资源上限
    setrlimit 设置 系统资源上限
    getrusage 获取 系统资源使用情况
    uselib 选择要使用的二进制 函数
    ioperm 设置端口I/O权限
    iopl 改变进程I/O权限级别
    outb 低级端口操作
    reboot 重新启动
    swapon 打开交换文件和设备
    swapoff 关闭交换文件和设备
    bdflush 控制bdflush守护进程
    sysfs 取核心支持的文件系统类型
    sysinfo 取得 系统信息
    alarm 设置进程的闹钟
    getitimer 获取 计时器
    setitimer 设置 计时器
    gettimeofday 取时间和时区
    settimeofday 设置时间和时区
    stime 设置 系统日期和时间
    time 取得系统时间
    times 取进程运行时间
    uname 获取当前UNIX系统的名称、版本和主机等信息
    vhangup 挂起当前终端
    nfsservctl 对NFS守护进程进行控制
    vm86 进入模拟8086模式
    create_module 创建可装载的模块项
    delete_module 删除可装载的模块项
    init_module 初始化模块
    query_module 查询模块信息
    *get_kernel_syms 取得核心符号,已被query_module代替

    内存管理

    brk 改变数据段空间的分配
    s brk 参见brk
    mlock 内存页面加锁
    munlock 内存页面解锁
    mlockall 调用进程所有内存页面加锁
    munlockall 调用进程所有内存页面解锁
    mmap 映射虚拟内存页
    munmap 去除内存页映射
    mremap 重新映射虚拟内存地址
    msync 将映射内存中的数据写回磁盘
    mprotect 设置内存映像保护
    getpagesize 获取页面大小
    sync 将内存 缓冲区数据写回硬盘
    cacheflush 将指定 缓冲区中的内容写回磁盘

    网络管理

    getdomainname 取域名
    setdomainname 设置域名
    gethostid 获取主机标识号
    sethostid 设置主机标识号
    gethostname 获取本主机名称
    sethostname 设置主机名称

    socket控制

    socketcall socket系统调用
    socket 建立socket
    bind 绑定socket到端口
    connect 连接远程主机
    accept 响应socket连接请求
    send 通过socket发送信息
    sendto 发送UDP信息
    sendmsg 参见send
    recv 通过socket接收信息
    recvfrom 接收UDP信息
    recvmsg 参见recv
    listen 监听socket端口
    select 对多路同步I/O进行轮询
    shutdown 关闭socket上的连接
    getsockname 取得本地socket名字
    getpeername 获取通信对方的socket名字
    getsockopt 取端口设置
    setsockopt 设置端口参数
    sendfile 在文件或端口间传输数据
    socketpair 创建一对已联接的无名socket

    用户管理

    getuid 获取用户标识号
    setuid 设置用户标志号
    getgid 获取组标识号
    setgid 设置组标志号
    getegid 获取有效组标识号
    setegid 设置有效组标识号
    geteuid 获取有效用户标识号
    seteuid 设置有效用户标识号
    setregid 分别设置真实和有效的的组标识号
    setreuid 分别设置真实和有效的用户标识号
    getresgid 分别获取真实的,有效的和保存过的组标识号
    setresgid 分别设置真实的,有效的和保存过的组标识号
    getresuid 分别获取真实的,有效的和保存过的 用户标识
    setresuid 分别设置真实的,有效的和保存过的 用户标识
    setfsgid 设置文件系统检查时使用的组标识号
    setfsuid 设置文件系统检查时使用的用户标识号
    getgroups 获取后补组标志清单
    setgroups 设置后补组标志清单

    进程间通信

    ipc  进程间通信总控制调用

    1、信号

    sigaction 设置对指定信号的处理方法
    sigprocmask 根据参数对信号集中的信号执行阻塞/解除阻塞等操作
    sigpending 为指定的被阻塞信号设置队列
    sigsuspend 挂起进程等待特定信号
    signal 参见signal
    kill 向进程或进程组发信号
    *sigblock 向被阻塞 信号掩码中添加信号,已被sigprocmask代替
    *siggetmask 取得现有阻塞 信号掩码,已被sigprocmask代替
    *sigsetmask 用给定 信号掩码替换现有阻塞信号掩码,已被sigprocmask代替
    *sigmask 将给定的信号转化为掩码,已被sigprocmask代替
    *sigpause 作用同 sigsuspend,已被sigsuspend代替
    sigvec 为兼容BSD而设的信号处理 函数,作用类似sigaction
    ssetmask ANSI C的信号处理 函数,作用类似sigaction

    2、消息

    msgctl 消息控制操作
    msgget 获取消息队列
    msgsnd 发消息
    msgrcv 取消息

           3、管道

    pipe 创建管道

    4、信号量

    semctl  信号量控制
    semget 获取一组 信号量
    semop  信号量操作

    5、共享内存

    shmctl 控制共享内存
    shmget 获取共享内存
    shmat 连接共享内存
    shmdt 拆卸共享内存






    展开全文
  • 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;
    }
    


     

    展开全文
  • 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。

    展开全文
  • Shell项目:命令解析器

    千次阅读 2018-09-16 13:05:18
    命令解析器:  提示信息:运行自己的Shell时,首先会像系统shell一样,输出一段提示信息, 从左到右依次是:登陆时的用户名,主机信息,和当前目录。  我们定义一个函数Put Flag()来实现, 这里涉及到几个...
  • 1、system(执行shell 命令) 相关函数 fork,execve,waitpid,popen 表头文件 #include 定义函数 int system(const char * string); 函数说明 system()会调用fork()产生子进程,由子进程来调用/bin/sh-c string来...
  • 编写简单的shell命令解析器

    千次阅读 2009-04-22 21:13:00
    编写简单的shell命令解析器环境:RedHat 9.0 内核 2.4.20vi 文书编辑器 6.1.320gcc 3.2.2-5 实现步骤: 第0步:写一个最简单的shell命令解释器,本程序取自APUE例1-5,命令不能带参数。 需要完成的
  • 我们知道,Class 文件的编译过程中并不包括传统编译中的连接步骤,一切方法调用在 Class 文件调用里面存储的都只是符号引用,而不是方法在实际运行时的内存布局入口地址,也就是说符号引用解析成直接引用的过程。...
  • Java调用WMIC命令的方法

    千次阅读 2010-05-12 16:21:00
    今天要写个远程重启服务的功能,为了开发速度,暂时定为Java代码+WMIC命令的方法,简单的说,就是利用Java调用本机应用程序的方法。涉及到的Java类有java.lang包里面的Runtime、Process、ProcessBuilder三个类,以及...
  • shell脚本【命令解析过程】

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

    千次阅读 2015-11-20 17:27:37
    Read 系统调用在用户空间中的处理过程 Linux 系统调用(SCI,system call interface)的实现机制实际上是一个多路汇聚以及分解的过程,该汇聚点就是 0x80 中断这个入口点(X86 系统结构)。也就是说,所有系统调用...
  • Java 代码调用ANT

    千次阅读 2011-12-19 11:36:19
    Apache Ant 是一个基于 Java 的开放源代码构建实用工具。人们常把它与 Make 进行比较,Make 长期以来一直用于帮助自动完成构建过程。经过不同版本的改进,Ant 已发展成一个丰富的功能库,使其成为适用于许多场合的...
  • Linux:objdump命令解析

    万次阅读 多人点赞 2018-09-03 16:05:43
    objdump命令是Linux下的反汇编目标文件或者可执行文件的命令,它以一种可阅读的格式让你更多地了解二进制文件可能带有的附加信息。 参数选项: --archive-headers -a 显示档案库的成员信息,类似ls -l将lib*....
  • 单片机串口实现字符串命令解析

    千次阅读 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]))) //用来计算结构体数组中,数组的个数。...
  • 一:下载安装包 ... ...二:建立几个文件夹,放视频和安装包(这样就可以直接调用我接下来的java代码实现视频转码) 三:安装jdk(配置环境) 四:安装gcc (配置环境)yum install gcc ...
  • 1. 方法 方法一:Runtime类 接 Java里面的lang包里(java.lang.Runtime)提供了一个...场景:在运行的java程序linux环境的linux命令 缺点:不能在任意主机上运行linux命令 使用参考: http://wangbaoaiboy.blog.163.co
  • 通过上一篇文章《Appium Android Bootstrap源码分析之控件AndroidElement》我们知道了Appium从pc端发送过来的命令如果是控件相关的话,最终目标控件在bootstrap中是以AndroidElement对象的方式呈现出来的,并且该...
  • 今天要写个远程重启服务的功能,为了开发速度,暂时定为Java代码+WMIC命令的方法,简单的说,就是利用Java调用本机应用程序的方法。涉及到的Java类有java.lang包里面的Runtime、Process、ProcessBuilder三个类,以及...
  • 非常有用的一些Linux命令解析

    千次阅读 2012-11-23 09:03:30
    top命令是Linux下常用的性能分析工具,能够实时显示系统中各个进程的资源占用状况,类似于Windows的任务管理。下面详细介绍它的使用方法。 认识top的显示结果 top命令的显示结果如下所示: top - 01:06:48 up ...
  • 本文介绍了系统调用的一些实现细节...然后,我们考察了Linux内核如何实现系统调用,以及执行系统调用的连锁反应:陷入内核,传递系统调用号和参数,执行正确的系统调用函数,并把返回值带回用户空间。最后讨论了如...
  • cJSON使用详细教程 | 一个轻量级C语言JSON解析器

    万次阅读 多人点赞 2020-01-01 21:20:24
    它采用完全独立于编程语言的文本格式来存储和表示数据,语法简洁、层次结构清晰,易于人阅读和编写,同时也易于机器解析和生成,有效的提升了网络传输效率。 JSON语法规则 JSON对象是一个无序的"名称/值"键值对的...
  • Matlab调用C++代码的具体mex实现详解

    万次阅读 2017-11-21 11:25:38
    在这里记录一下自己的挖坑、填坑过程。从mex环境到与成功调用在具体的实现中,由于matlab具有很强的矩阵运算能力。...当然这也完全使用C语言的代码调用实现。一、首先,我们需要有自己的编程环境,我的是matlab2017+Mi
  • Python3.X 爬虫实战(静态下载器与解析器

    万次阅读 多人点赞 2017-06-17 01:05:34
    这一篇内容主要延续上一篇[《Python3.X 爬虫实战(先爬起来嗨)》](http://blog.csdn.net/yanbober/article/details/73162298),重点偏向于爬虫爬取静态页面的下载器与解析器常用套路引导,主要适用于理解爬虫流程和...
  • 使用cxf客户端生成java代码调用

    千次阅读 2019-05-15 19:01:36
    下载的首页会列出几个版本,如果要下载以前的版本,就滑到下载页面的底部----Previous releases标题下,这里官网提供了两个链接,小编英文比较菜,谷歌翻译过来是费孵化发布以及孵化发布,不清楚区别在哪里,...
  • read系统调用深度剖析

    万次阅读 2014-08-12 17:10:15
    本文转自:http://blog.csdn.net/unbutun/article/details/6101354#ibm-pconRead 系统调用在用户空间中的处理过程Linux 系统调用(SCI,system call interface)的实现机制实际上是一个多路汇聚以及分解的过程,该...
  • 但是有局限性,那篇文章是在windows下实现的,linux不能直接使用,并且一次命令只能启动一个控制,就是说你有两个控制需要后台启动,无法用一条命令启动.本片文章为解决这个需求的解决方案.但是只适合linux版...
  • 用 C 语言实现命令 cp、df、mkdir、rm...本文主要讲述 cp、mkdir、rm、tac、df 五个命令主要功能的模拟实现代码。让读者学会使用 strace 来跟踪系统调用的使用情况。加深读者对操作系统的认识与理解,引导读者学习
  • 这个作业我是从五月上旬开始着手准备的,因为在这期间有几门考试,所以从装系统到完成作业前前后后大概用了半个多月的时间。...虽然在做作业的时候解决了很多与添加系统调用无关的棘手事情,但我实实在在的通过解...
  • Druid SQL 解析器的解析过程

    千次阅读 2017-05-17 07:09:46
    这篇文尝试近距离地探究 Druid SQL 解析器如何工作。 Demo 代码 以这份代码为例 /** * * * @author beanlam * @date 2017年1月10日 下午11:06:26 * @version 1.0 * */ public class ParserMain { ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 164,609
精华内容 65,843
关键字:

哪些代码调用了系统的命令解析器