山东大学计算机科学与技术学院操作系统实验3 进程综合实验(Shell) 独立实验代码

本文详细描述了山东大学计算机科学与技术学院操作系统实验中的进程综合实验,涉及C语言代码,包括cmake配置、信号处理、命令解析(如split_string,extract_single_command等)、管道操作、后台执行以及文件重定向等功能。
摘要由CSDN通过智能技术生成

山东大学计算机科学与技术学院操作系统实验3 进程综合实验(Shell) 独立实验代码

2024 3.25 请勿直接复制(包括代码和实验解释)

本代码在配置cmakelists时需要添加一些选项(一个示例)

这里假设你的源代码文件为main.c

cmake_minimum_required(VERSION 3.0)
project(main)
add_executable(main main.c)
find_package(Curses REQUIRED)
include_directories(${CURSES_INCLUDE_DIR})
target_link_libraries(main ${CURSES_LIBRARIES} readline)

实验代码函数解释

  • 后台运行命令的输出会输出到log.txt中
  • split_string 函数:
    该函数首先通过 strtok 函数将输入字符串按照 “|” 分割成子字符串。 然后动态分配内存来存储这些子字符串,并返回子字符串数组。
  • extract_single_command 函数:
    该函数使用 strtok 函数将单个命令字符串按照空格分割成参数数组。 参数数组中的每个元素都是一个命令参数。
  • sigint_handler 函数:
    该函数是一个信号处理函数,用于捕获 Ctrl+C 信号。 当捕获到 Ctrl+C 信号时,会打印一条信息并发送 SIGINT 信号给子进程。
  • parse_output_string 和 parse_input_string 函数:
    这两个函数用于解析输出重定向和输入重定向的命令。 它们会将命令字符串按照 “>” 和 “<” 进行分割,并返回文件名和命令。
  • judge_command_length 函数:
    该函数用于判断命令长度是否合法,如果超过指定长度则打印错误信息并返回 0。
  • if_in_sub 函数:
    该函数用于判断命令中是否包含后台运行的标志符 “@”,如果包含则返回 1,否则返回 0。
  • execute_out_redirection 和 execute_input_redirection 函数:
    这两个函数用于执行输出重定向和输入重定向的命令。 它们会首先解析命令字符串,然后进行文件操作和进程替换以实现重定向。
  • execute_normal_command 函数:
    该函数用于执行普通的命令,如果是后台执行则将输出重定向到 log.txt 文件。 execute_pipe_recursion 和 execute_pipe_command 函数:
    这两个函数用于执行管道命令。execute_pipe_command 首先调用 split_string 函数分割多个命令,然后递归调用 execute_pipe_recursion 来执行管道命令。
  • main:
    主函数中包含了一个循环,不断读取用户输入的命令,并进行解析和执行。 在主循环中进行了命令长度判断、后台运行判断、创建子进程、信号处理等逻辑,以实现简单的命令行解析器功能。

实验源代码

放弃了对管道符的递归实现(找不动bug了)

#include<unistd.h>
#include<stdio.h>
#include<signal.h>
#include <stdlib.h>
#include <termios.h>
#include <string.h>
#include <sys/types.h>
#include <wait.h>
#include <readline/readline.h>
#include <readline/history.h>
#include <fcntl.h>
#include <ncurses.h>
int if_first_pipe=0;
char** split_string(const char *input_string, int *length) {
    char *token;
    char *input_copy = strdup(input_string);
    char **result = NULL;
    int count = 0;
    token = strtok(input_copy, "|");
    while (token != NULL) {
        count++;
        token = strtok(NULL, "|");
    }
    result = (char**)malloc(count * sizeof(char*));
    if (result == NULL) {
        *length = 0;
        return NULL;  // 内存分配失败
    }
    strcpy(input_copy, input_string);
    token = strtok(input_copy, "|");
    for (int i = 0; i < count; i++) {
        result[i] = strdup(token); 
        token = strtok(NULL, "|");
    }

    *length = count;
    free(input_copy);
    return result;
}
void extract_single_command(char*command,char**tokens){
    char *token; // 动态分配空间
    int i = 0;
    token = strtok(command, " ");
    while (token != NULL) {
        tokens[i] = strdup(token); // 使用 strdup 复制字符串并将指针存入数组
        i++;
        token = strtok(NULL, " ");
    }
    tokens[i] = NULL; // 在数组末尾添加一个 NULL 指针作为结束标记
}
void removeAtSymbol(char *str) {//用于后台运行的标志符
    int i, j = 0;
    for (i = 0; str[i] != '\0'; i++) {
        if (str[i] != '@') {
            str[j++] = str[i];
        }
    }
    str[j] = '\0';
}
int child_pid;//save the pid of the subprocess
void sigint_handler(int signum) {
    printf("\nCtrl+C pressed. Exiting command\n");
    kill(child_pid, SIGINT);
}
void parse_output_string(const char *input_string, char result[2][100]) {
    char *token;
    char input_copy[100];
    strcpy(input_copy, input_string);
    token = strtok(input_copy, ">");
    strcpy(result[0], token);

    token = strtok(NULL, ">");
    if (token != NULL) {
        // 去除右侧字符串的开头空格
        while (*token == ' ') {
            token++;
        }
        int end = strlen(token) - 1;
        // 去除右侧字符串的末尾空格
        while (end >= 0 && token[end] == ' ') {
            token[end] = '\0';
            end--;
        }
        strcpy(result[1], token);
    } else {
        strcpy(result[1], "");
    }
}
void parse_input_string(const char *input_string, char result[2][100]) {
    char *token;
    char input_copy[100];
    strcpy(input_copy, input_string);
    token = strtok(input_copy, "<");
    if (token != NULL) {
        // 去除右侧字符串的开头空格
        while (*token == ' ') {
            token++;
        }
        int end = strlen(token) - 1;
        // 去除右侧字符串的末尾空格
        while (end >= 0 && token[end] == ' ') {
            token[end] = '\0';
            end--;
        }
        strcpy(result[0], token);
    } else {
        strcpy(result[0], "");
    }
    token = strtok(NULL, "<");
    strcpy(result[1], token);

}
int judge_command_length(char*command){
    if(strlen(command)>=100){
            printf("\033[1;31mError -> input command length over the limit\n");
            printf("\033[0m");
            return 0;
    }
    return 1;
}
int if_in_sub(char* command){
    if(strchr(command,'@')!=NULL){
        return 1;
    }
    return 0;
}
void execute_out_redirection(char *command,int background){//
    char result[2][100];
    parse_output_string(command,result);
    int log_fd;
    int stdout_backup;
    log_fd=open(result[1],O_APPEND | O_CREAT | O_WRONLY,0644);
    if(log_fd<0){
        printf("\033[1;33mError -> Failed to log to the file\n");
        return;
    }
    stdout=dup(1);
    if(dup2(log_fd,1)<0){
        printf("\033[1;31mError -> can not redirecting to log file\n");
        return;
    }
    char **tokens=(char **)malloc(20 * sizeof(char*));
    extract_single_command(result[0],tokens);    
    if(execvp(tokens[0],tokens)==-1){
        printf("\033[1;31mError -> Cannot find command\n");
    }
    dup2(stdout_backup,1);
    close(log_fd);
    close(stdout_backup);
}
void execute_input_redirection(char *command,int background){//
    char result[2][100];
    parse_input_string(command,result);
    int input_fd;
    int stdin_backup;
    input_fd=open(result[0],O_RDONLY);
    if(input_fd<0){
        printf("\033[1;33mError -> Failed to open the file\n");
        return;
    }
    stdout=dup(1);
    if(dup2(input_fd,0)<0){
        printf("\033[1;31mError -> can not read the file\n");
        return;
    }
    //
    char **tokens=(char **)malloc(20 * sizeof(char*));
    extract_single_command(result[1],tokens);    
    if(execvp(tokens[0],tokens)==-1){
        printf("\033[1;31mError -> Cannot find command\n");
    }
    dup2(stdin_backup,1);
    close(input_fd);
    close(stdin_backup);
}
void execute_normal_command(char *command,int background){//
    int log_fd;
    int stdout_backup;
    char result[2][100];
    if(background==1){
        log_fd=open("log.txt",O_APPEND | O_CREAT | O_WRONLY,0644);
        if(log_fd<0){
            printf("\033[1;31mError -> Failed to log to the file\n");
            return;
        }
        stdout=dup(1);
        if(dup2(log_fd,1)<0){
            printf("\033[1;31mError -> can not redirecting to log file\n");
            return;
        }
    }
    char **tokens=(char **)malloc(20 * sizeof(char*));
    extract_single_command(command,tokens);
    if(execvp(tokens[0],tokens)==-1){
        printf("\033[1;31mError -> Cannot find command\n");
    }
    if(background==1){
        dup2(stdout_backup,1);
        close(log_fd);
        close(stdout_backup);
    }
}
void test(){
    printf("\033[1;31mthis is a test\n");
    printf("\033[01m");
}
int pipe_prev[2];
void execute_pipe_recursion(char *commands[], int n,int*pipe_prev) {
    int pipefd[2];
    pid_t pid1, pid2;
    char **tokens = (char **)malloc(20 * sizeof(char *));
            extract_single_command(commands[0], tokens);
    if (pipe(pipefd) == -1) {
        perror("pipe");
        exit(EXIT_FAILURE);
    }

    pid1 = fork();
    if (pid1 == -1) {
        perror("fork");
        exit(EXIT_FAILURE);
    } else if (pid1 == 0) {
        // Child process 1: ls
        close(pipefd[0]); // Close reading end of the pipe
        dup2(pipefd[1], STDOUT_FILENO); // Redirect stdout to the writing end of the pipe
        close(pipefd[1]); // Close the writing end of the pipe

        char *ls_args[] = {"ls", NULL};
        execvp(tokens[0], tokens);
    } else {
            // Child process 2: wc -w
            wait(NULL);
            close(pipefd[1]); // Close writing end of the pipe
            dup2(pipefd[0], STDIN_FILENO); // Redirect stdin to the reading end of the pipe
            close(pipefd[0]); // Close the reading end of the pipe

            char *wc_args[] = {"wc", "-w", NULL};
            extract_single_command(commands[1], tokens);
            execvp(tokens[0], tokens);
    }
}
void execute_pipe_command(char *command, int background) {
    char **command_set;
    int command_num;
    if (pipe(pipe_prev) == -1) {
        perror("pipe");
        exit(EXIT_FAILURE);
    }
    command_set=split_string(command,&command_num);
    execute_pipe_recursion(command_set,command_num,pipe_prev);
}
bool if_with_char(char *s,char c){
    char *tmp=s;
    while(*tmp!='\0'){
        if(*tmp==c){
            return 1;
        }
        tmp++;
    }
    return 0;
}
int main(){
    int background;
    char*command;
    rl_attempted_completion_function = NULL;
    char cwd[1024]; // 声明一个足够大的字符数组来存储当前工作目录的路径
    using_history();
    while(1){
        //
        if_first_pipe=0;
        int current_index=0;
        int command_length=0;
        background=0;
        getcwd(cwd,sizeof(cwd));
        printf("\033[1;34m%s",cwd);
        printf("\033[0m");
        command=readline("$ ");
        add_history(command);
        //判断用户输入的命令长度的合法
        if(judge_command_length(command)==0){
            continue;
        }
        //解析命令
        //judge if background
        background=if_in_sub(command);
        removeAtSymbol(command);
        int a=fork();
        if(a<0){
            printf("\033[1;31mError -> failed to create process\n");
            printf("\033[0m");
            continue;
        }else if(a==0){//
            signal(SIGINT,SIG_DFL);
            int tmp_back=background;
            char tmp_command[100];
            strcpy(tmp_command,command);
            if(if_with_char(tmp_command,'|')){
                execute_pipe_command(tmp_command,tmp_back);
            }else if(if_with_char(tmp_command,'<')){
                execute_input_redirection(tmp_command,tmp_back);
            }else if(if_with_char(tmp_command,'>')){
                execute_out_redirection(tmp_command,tmp_back);
            }else{
                execute_normal_command(tmp_command,tmp_back);
            }
            exit(EXIT_SUCCESS);
        }else{//父进程
            child_pid=a;
            signal(SIGINT,sigint_handler);
            if(background==1){
                //输出输出到对应的log.txt文件中
            }else{//在父进程中等待执行
                wait(NULL);
            }
        }
        free(command);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值