多进程|sh2.c 实现文件重定向

1.题目要求

实现shell程序,要求在第1版的基础上,添加如下功能:

  • 实现文件重定向
 # 执行sh2
  $ ./sh2
  
  # 执行命令echo,并将输出保存到文件log中
  > echo hello >log
  
  # 打印cat命令的输出结果
  > cat log
  hello

2.解决思路

sh1.c的基础上,在创建子进程之后,需要判断当前键入的命令是否需要文件重定向,如果是需要实现文件重定向,反之按照之前的系统调用execvp装入程序执行。因此在结构体中增加一个char *型变量output用于存储输出重定向的文件路径,初始化为NULL。在分割命令字符串子函数while循环中增加判断,调用库函数strchr判断是否含有字符>,若否返回NULL,若是返回自>开始的余下所有字符子串,将返回值res指针加1存储到output,这样只需要判断output是否为NULL来判断是否需要重定向。若需要则调用handle_redirect函数,先打开文件,再使用dup2系统调用,文件描述符1是标准输出,fd指向文件实现标准输出重定向到文件。

3.代码

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

#define SIZE 256
#define MAX_ARGC 16

struct Command {
  int argc;
  char *argv[MAX_ARGC];
  char *input;    //用于重定向输入
  char *output;   //用于重定向输出
} commands;


void panic(char *message) {
  perror(message);
  exit(EXIT_FAILURE);
}

// 将 command 以空格作为分割符分割
void parse_command(char *command) {
  // char line[SIZE];       // 注意不能这样写
  char *line = (char *)malloc(sizeof(command));
  strcpy(line, command); 

  // 清理内存
  commands.argc = 0;
  for(int i = 0; i < MAX_ARGC; i++)
    commands.argv[i] = NULL;
  commands.input = NULL;
  commands.output = NULL;

  char *word;
  char *delim = " \n";
  word = strtok(line, delim);
  while(word != NULL) {
    char *output = strchr(word, '>');
    char *input = strchr(word, '<');
    if(output) 
      commands.output = output + 1;
    else if(input) 
      commands.input = input + 1;
    else 
      commands.argv[commands.argc++] = word;
    word = strtok(NULL, delim);
  }
}

// 处理内置命令cd、pwd、exit
void handle_built_in_command() {
  if(strcmp(commands.argv[0], "cd") == 0) {
    int error = chdir(commands.argv[1]);
    if(error < 0) 
      perror("cd");
    else {
      char *path = getcwd(NULL, 0);
      printf("current working directory: %s\n", path);
      free(path);
    }
  }
  else if(strcmp(commands.argv[0], "pwd") == 0) {
    char *path = getcwd(NULL, 0);
    printf("current working directory: %s\n", path);
    free(path);
  }
  else if(strcmp(commands.argv[0], "exit") == 0) {
    exit(0);
  }
}

// 处理文件重定向
void handle_redirect() {
  if(commands.output) {
    int fd = open(commands.output, O_CREAT | O_RDWR);
    if(fd < 0) 
      panic("open");
    dup2(fd, 1);
    close(fd);
  }
  if(commands.input) {
    int fd = open(commands.input, O_CREAT | O_RDWR);
    if(fd < 0) 
      panic("open");
    dup2(fd, 0);
    close(fd);
  }
}

void mysys(char *command) {
  parse_command(command);

  // 单独处理内置命令cd、pwd、exit
  if(strcmp(commands.argv[0], "cd") == 0 || strcmp(commands.argv[0], "pwd") == 0 || strcmp(commands.argv[0], "exit") == 0) {
    handle_built_in_command();
    return ;
  }

  pid_t pid;
  pid = fork();
  if(pid == 0) {
    // 处理需要文件重定向的情况
    if(commands.input || commands.output) {
      handle_redirect();
    }

    int error = execvp(commands.argv[0], commands.argv);
    if(error < 0)
      perror("execvp");
  }
  wait(NULL);
}


int main() {
  char command[SIZE];
  int count;

  while(1) {
    write(1, ">", sizeof(">"));
    count = read(0, command, sizeof(command));
    command[count] = 0;
    mysys(command);
  }
  return 0;
}

4.运行结果

$ gcc sh2.c -o sh2
$ ./sh2
>echo hello >log
>cat log
cat: log: 权限不够
>sudo cat log
[sudo] (用户名XXX) 的密码: 
hello
>exit
  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值