简易Shell

  1. 出处:课程《Unix高级环境编程》
    扩展书籍《Unix环境高级编程》的例1.5、1.8
  2. 功能需求
    • 程序从控制台执行,启动后显示一个命令提示符“->”。用户可以通过给特定的环境变量赋值来改变命令提示符形式。
    • 通过某个特殊的命令或按键组合可以正常地关闭本程序。
    • 提供后台运行机制。用户提交的任务可以通过某种指示使之在后台运行,例如-> bg job1 将使任务job1在后台运行,并马上返回给用户一个新的提示符。
    • 提供输出重定向。通过指定文件名将任务的所有输出覆盖写到文件中而不是送到标准输出上。
    • 提供输入重定向。通过指定文件名使得任务从相应的文件中去获取所需的数据,而不是从标准输入上。
  3. 要求
    • 请使用C语言完成作业,不允许使用其他语言。
    • 本程序应独立完成,不允许小组一起完成。
    • 文档用纯文本格式或TEX书写,其他格式无效。

初次完成是程序,又蠢又长,还有bug,贴下来铭记要好好学习

/** * zsh.c : 简单的shell程序 * * created: zh * date: 2014.5.28~2014.6.8 * version1.0: first version * * *********************************************/

#include <stdlib.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <pwd.h>
#include <libgen.h>
#include <sys/types.h>

#include <readline/readline.h>
#include <readline/history.h>


int do_exit(int, char **);
int do_export(int, char **);
int do_echo(int, char **);
int do_cd(int, char **);
int do_history(int, char **);

void do_line(char *line);
void do_pipe_cmd(char *line);
int do_simple_cmd(int argc, char **argv, int prefd[], int postfd[]);
int isBgProgram(int len, char *line[10]);
char **arrayToPointer(int num, char *argv[10]);
int predo_for_redirect(int argc, char **argv, int *re);

char lastdir[100];
char **arg;

typedef int (*buildin_cmd_handle) (int, char **);
typedef struct {
    const char *cmd;
    buildin_cmd_handle handle;
} CMD_ENTRY;

const CMD_ENTRY buildin_cmd_table[] = {
    {"exit", do_exit},
    {"cd", do_cd},
    {"echo", do_echo},
    {"export", do_export},
    {"history", do_history},
    {0, 0}
};


void history_finish()
{
    append_history(history_length, "/tmp/msh_history");
    history_truncate_file("/tmp/msh_history", history_max_entries);
}

/** * 用于获取函数句柄 */
buildin_cmd_handle get_cmd_handle(const char *cmd)
{
    int i = 0;
    while (buildin_cmd_table[i].cmd) {
    if (strcmp(buildin_cmd_table[i].cmd, cmd) == 0)
        return buildin_cmd_table[i].handle;
    i++;
    }
    return 0;
}

/** * 用于显示历史输入信息 */
void display_history_list()
{
    HIST_ENTRY **h = history_list();
    if (h) {
    int i = 0;
    while (h[i]) {
        printf("%d: %s\n", i, h[i]->line);
        i++;
    }
    }
}

/*** * 功能:执行exit命令,退出shell解释器 * 输入:argc:命令行字符串数目;argv:命令行字符串组 */
int do_exit(int argc, char **argv)
{
    int val = 0;
    if (argc > 1)
    val = atoi(argv[1]);
    //free_resource();
    history_finish();
    exit(val);
    return 0;
}
/*** * 功能:执行cd命令,切换shell当前路径 * 输入:argc:命令行字符串数目;argv:命令行字符串组 */
int do_cd(int argc, char **argv)
{
    char *dir;
    char cwd[100];

    if (argc == 1) {
    if (!(dir = getenv("HOME"))) {
        printf("cd: %s\n", strerror(errno));
        return -1;
    }
    } else if (argc == 2) {
    if (strcmp(argv[1], "-") == 0) {
        dir = lastdir;
    } else if (strcmp(argv[1], "~") == 0) {
        if (!(dir = getenv("HOME"))) {
        printf("cd: %s\n", strerror(errno));
        return -1;
        }
    } else
        dir = argv[1];
    } else {
    printf("Usage: cd [dir]\n");
    return -1;
    }
    getcwd(cwd, 99);
    if (chdir(dir) == -1) {
    printf("cd: %s\n", strerror(errno));
    return -1;
    }
    strcpy(lastdir, cwd);
    return 0;
}
/*** * 功能:执行export命令,导入或显示环境变量 * 输入:argc:命令行字符串数目;argv:命令行字符串组 */
int do_export(int argc, char **argv)
{
    int i = 1;
    char *p;
    while (argv[i]) {
    if ((p = strchr(argv[i], '='))) {
        *p = 0;
        if (strpbrk(argv[i], "~`!@#$%^&*()-_+=|\\{}[];:'\"<>,.?/")) {
        *p = '=';
        printf("export: %s: not a valid indentifier\n", argv[i]);
        i++;
        continue;
        }

        if (setenv(argv[i], p + 1, 1) == -1)
        printf("export: %s\n", strerror(errno));
        *p = '=';
    }
    i++;
    }
    return 0;
}
/*** * 功能:执行echo命令,回显变量 * 输入:argc:命令行字符串数目;argv:命令行字符串组 */
int do_echo(int argc, char **argv)
{
    int i = 1;
    int j;
    int argn = 0;
    int arge = 0;
    if (argv[1]) {
    if (strcmp(argv[1], "-n") == 0) {
        argn = 1;
        i = 2;
    } else if (strcmp(argv[1], "-e") == 0) {
        arge = 1;
        i = 2;
    } else if ((strcmp(argv[1], "-ne") == 0)
           || (strcmp(argv[1], "-en") == 0)) {
        argn = arge = 1;
        i = 2;
    }
    }
    j = i;
    while (argv[i]) {
    if (i > j)
        printf(" %s", argv[i]);
    else
        printf("%s", argv[i]);
    i++;
    }
    if (argn == 0)
    printf("\n");

    return 0;
}
/*** * 功能:执行history命令,显示历史信息 * 输入:argc:命令行字符串数目;argv:命令行字符串组 */
int do_history(int argc, char **argv)
{
    display_history_list();
    return 0;
}

/** * 设置shell命令提示符,可通过设置环境变量envprompt改变 */
void setShellPrompt(char *prompt)
{
    char host[100];
    char cwd[100];
    struct passwd *pwp;
    if (gethostname(host, 99) == -1) {
    strcpy(host, "unknown");
    } else {
    char *p = strchr(host, '.');
    if (p)
        *p = 0;
    }
    if (!getcwd(cwd, 99)) {
    strcpy(cwd, "unknown");
    } else {
    if (strcmp(cwd, "/") != 0)
        strcpy(cwd, basename(cwd));
    }
    pwp = getpwuid(getuid());
    sprintf(prompt, "[%s@%s %s]# ", (pwp ? pwp->pw_name : "unknown"), host,
        cwd);
}

/** * 释放程序中申请的内存 */
void free_source(int num, char **name)
{
    int n;
    for (n = 0; n < num; n++) {
        free(name[n]);
        name[n] = 0;
    }
}


int main(int argc, char **argv)
{

    char prompt[200];
    setShellPrompt(prompt);
    //signal(SIGINT,SIG_IGN);

    char *line;         //char line[300];//
    line = malloc(200);
    memset(line, 0, 200);

    char *env;
    env = malloc(strlen(prompt) + 11);
    strcpy(env, "envprompt=");
    strcat(env, prompt);
    putenv(env);

    char *tmpline;
    tmpline = malloc(strlen(prompt));
    while (1) {

    tmpline = getenv("envprompt");
    if (!(line = readline(tmpline))) {
        break;
    }
    if (*line) {
        add_history(line);
    }
    //printf("%s + %d\n",line, strlen(line));
    do_line(line);
    putenv(line);

    }
    free(env);
    env = NULL;
    free(tmpline);
    tmpline = NULL;
    free(line);
    line = NULL;

    return 0;

}

/** * 功能:解析输入命令,并按";"解析命令,并传入到函数do_pipe_cmd() * 输入:原始命令行 */
void do_line(char *line)
{
    int len, i, j = 0, k = 0;
    char *argv[20];
    int m = 0;
    len = strlen(line);
    //printf("---%s---||---%d---\n",line,len);
    for (i = 0; i <= len; i++) {
    if (line[i] == ';') {
        if ((argv[k] = (char *) malloc((i - j) * sizeof(char))) == 0) {
        fprintf(stderr, "error! can't malloc space for argv\n");
        } else {
        memset(argv[k], 0, (i - j) * sizeof(char));
        for (m = 0; m < i - j; m++) {
            argv[k][m] = line[j + m];
        }
        argv[k][m] = '\0';
        //printf("---%s---\n",argv[k]);
        do_pipe_cmd(argv[k]);
        j = i + 1;
        k++;

        }
    } else if (line[i] == '\n' || (i == len && i != 0)) {
        if ((argv[k] = (char *) malloc((i - j) * sizeof(char))) == 0) {
        fprintf(stderr, "error! can't malloc space for argv\n");
        } else {
        memset(argv[k], 0, (i - j) * sizeof(char));
        //printf("(i - j) * sizeof(char) is %d\n",(i - j) * sizeof(char)); 
        //printf("---%s---%d---%d---\n",argv[k],m,strlen(argv[k]));
        for (m = 0; m < i - j; m++) {
            argv[k][m] = line[j + m];
        }
        argv[k][m] = '\0';
        //printf("---%s---%d---%d---\n",argv[k],m,strlen(argv[k]));
        do_pipe_cmd(argv[k]);
        j = i;
        k++;
        }
    }
    }
    free_source(k - 1, argv);

}

/** * 功能:解析输入命令,并按"|"解析简单命令,并传入到函数do_simple_cmd() * 输入:函数do_line()解析后的简单命令 */
void do_pipe_cmd(char *line)
{
    //printf("the line in pipe_cmd is %s\n",line);
    int len, i, j = 0, k = 0;
    char *argv[20];
    int m;
    len = strlen(line);

    int prepipe = 0;
    int prefd[2];
    int postfd[2];
    char *args[10];
    int num = 0;

    for (i = 0; i <= len; i++) {
    if (line[i] == '|') {
        if ((argv[k] = (char *) malloc((i - j) * sizeof(char))) == 0) {
        fprintf(stderr, "error! can't malloc space for argv\n");
        } else {
        memset(argv[k], 0, (i - j) * sizeof(char));
        for (m = 0; m < i - j; m++) {
            argv[k][m] = line[j + m];
        }
        argv[k][m] = '\0';
        num = add_arg(argv[k], args);

        j = i + 1;
        k++;

        pipe(postfd);
        if (prepipe)
            do_simple_cmd(num, arrayToPointer(num, args), prefd,
                  postfd);
        else
            do_simple_cmd(num, arrayToPointer(num, args), 0,
                  postfd);
        prepipe = 1;
        prefd[0] = postfd[0];
        prefd[1] = postfd[1];
        }
    } else if (i == len && i != 0) {
        if ((argv[k] = (char *) malloc((i - j) * sizeof(char))) == 0) {
        fprintf(stderr, "error! can't malloc space for argv\n");
        } else {
        memset(argv[k], 0, (i - j) * sizeof(char));
        for (m = 0; m < i - j; m++) {
            argv[k][m] = line[j + m];
        }
        argv[k][m] = '\0';
        num = add_arg(argv[k], args);
        j = i;
        k++;

        pipe(postfd);
        if (prepipe) {
            do_simple_cmd(num, arrayToPointer(num, args), prefd,
                  postfd);

        } else {
            do_simple_cmd(num, arrayToPointer(num, args), 0, 0);
        }
        }
    }
    }
    free_source(k - 1, argv);
    free_source(k - 1, args);
    free_source(num, arg);

}
/** * 功能:执行简单命令 * 输入:argc:命令行字符串数目;argv:命令行字符串组 * prefd,postfd:管道参数 */
int do_simple_cmd(int argc, char **argv, int prefd[], int postfd[])
{
    int pid;
    int status;
    buildin_cmd_handle hd;
    if (argc == 0)
    return 0;

    if (prefd == 0 && postfd == 0) {

    if ((hd = get_cmd_handle(argv[0]))) {
        if (predo_for_redirect(argc, argv, 0))
        return 1;
        (*hd) (argc, argv);
        return 0;
    }
    }

    if ((pid = fork()) == 0) {  //子进程
    // 重置signal INT handle为默认
    int redirect = 0;
    signal(SIGINT, SIG_DFL);

    if (predo_for_redirect(argc, argv, &redirect))
        exit(1);

    if (redirect != 1 && prefd) {   
        close(prefd[1]);
        if (prefd[0] != STDIN_FILENO) {
        dup2(prefd[0], STDIN_FILENO);
        close(prefd[0]);
        }
    }
    if (redirect != 2 && postfd) {  
        close(postfd[0]);
        if (postfd[1] != STDOUT_FILENO) {
        dup2(postfd[1], STDOUT_FILENO);
        close(postfd[1]);
        }
    }
    if ((hd = get_cmd_handle(argv[0]))) {
        (*hd) (argc, argv);
        exit(0);
    }

    char buffer[100];
    if (file_exist(argv[0], buffer)) {
        execv(buffer, argv);
    } else {
        fprintf(stderr, "-zsh: %s: command not found\n", argv[0]);
        exit(0);
    }
    } else if (pid < 0) {

    } else {
        if (isBgProgram(argc, argv) == 0) {
            //printf("this is not bg\n");
            waitpid(pid, &status, 0);
        }
    }


    if (postfd) {       
    close(postfd[1]);   // 关闭这个fd.
    }
    return 0;
}

/** * 将指针数组转换为二维指针,恕我学识浅薄,尚不明白为何一定要这样, * 只是这样才能通过编译....容我再学习 */
char **arrayToPointer(int num, char *argv[10])
{
    arg = (char **) malloc((num) * sizeof(char *));
    memset(arg,0, (num)*sizeof(char *));
    int p = 0;
    for (p = 0; p < num && num < 10; p++) {
    arg[p] = argv[p];
    }
    return arg;
}

/** * 功能:判断简单命令是否需要后台运行,通过“&”判别 */
int isBgProgram(int len, char **line)
{
    if (strcmp(line[len - 1], "&") == 0) {
    line[len - 1] = "\0";
    return 1;
    }
    return 0;
}

/** * 功能:将简单命令(字符串)转换为字符串数组,数组每个元素即为一个标记 */
int add_arg(char *line, char *args[10])
{

    int len, i, j = 0, k = 0;
    //char *args[10];
    len = strlen(line);
    int m = 0;

    char *newline;

    if ((newline = (char *) malloc(len * sizeof(char))) == 0) {
    fprintf(stderr, "error! can't malloc space for argv\n");
    }
    for (i = 0; i <= len;) {
    if (line[i] == ' ' && line[i + 1] == ' ' || line[i] == '\t')
        i++;
    else {
        newline[j] = line[i];
        j++;
        i++;
    }
    }
    i = 0;
    while (newline[i] == ' ' || newline[i] == '\t')
    i++;

    j = i;          //重新置为零
    for (; i <= len; i++) {
    if (newline[i] == ' ') {
        if ((args[k] = (char *) malloc((i - j) * sizeof(char))) == 0) {
        fprintf(stderr, "error! can't malloc space for argv\n");
        } else {
        memset(args[k], 0, (i - j) * sizeof(char));
        for (m = 0; m < i - j; m++) {
            args[k][m] = newline[j + m];
        }
        args[k][m] = '\0';
        j = i + 1;
        k++;
        }
    } else if (i == len) {
        if ((args[k] = (char *) malloc((i - j) * sizeof(char))) == 0) {
        fprintf(stderr, "error! can't malloc space for argv\n");
        } else {

        for (m = 0; m < i - j; m++) {
            args[k][m] = newline[j + m];
        }
        args[k][m] = '\0';
        j = i;
        k++;
        }
    }
    }
    return k;
}

/** * 功能:判断文件是否存在 */
int file_exist(const char *file, char *buffer)
{
    int i = 0;
    const char *p;
    const char *path = getenv("PATH");

    p = path;
    while (*p != 0) {
    if (*p != ':')
        buffer[i++] = *p;
    else {
        buffer[i++] = '/';
        buffer[i] = 0;
        strcat(buffer, file);
        if (access(buffer, F_OK) == 0)
        return 1;
        i = 0;
    }
    p++;
    }
    const char *p2 = getenv("PWD");

    p = p2;
    i = 0;
    while (*p != 0) {
    buffer[i++] = *p;
    p++;
    }
    buffer[i++] = '/';
    buffer[i] = 0;
    strcat(buffer, file);
    if (access(buffer, F_OK) == 0)
    return 1;
    return 0;
}
/** * 功能:完成重定向功能 */
int predo_for_redirect(int argc, char **argv, int *re)
{
    int i;
    int redirect = 0;
    for (i = 1; i < argc; i++) {
    if (strcmp(argv[i], "<") == 0) {
        redirect = 1;
        argv[i] = 0;
        break;
    } else if (strcmp(argv[i], ">") == 0) {
        redirect = 2;
        argv[i] = 0;
        break;
    }
    }
    if (redirect) {     
    if (argv[i + 1]) {
        int fd;
        if (redirect == 2) {
            if ((fd =
                 open(argv[i + 1], O_WRONLY | O_CREAT | O_TRUNC,
                  S_IRUSR | S_IWUSR)) == -1) {
                fprintf(stderr, "Open out %s failed\n", argv[i + 1]);
                return 1;
            }
            dup2(fd, STDOUT_FILENO);
        } else {        //redirect == 1
            if ((fd =
                 open(argv[i + 1], O_RDONLY,
                  S_IRUSR | S_IWUSR)) == -1) {
                fprintf(stderr, "Open in %s failed\n", argv[i + 1]);
                return 1;
            }
            dup2(fd, STDIN_FILENO);
        }
    } else {
        fprintf(stderr, "Bad redirect, need more arg\n");
        return 1;
    }
    }
    if (re)
    *re = redirect;
    return 0;
}

你可能感兴趣的:(linux)