多进程|sh1.c 取用户输入的命令,调用函数 mysys 执行用户的命令

1.题目要求

  • 该程序读取用户输入的命令,调用函数mysys(上一个作业)执行用户的命令,示例如下
# 编译sh1.c
$ cc -o sh1 sh1.c

# 执行sh1
$ ./sh 

# sh1打印提示符>,同时读取用户输入的命令echo,并执行输出结果
> echo a b c
a b c

# sh1打印提示符>,同时读取用户输入的命令cat,并执行输出结果
> cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
  • 请考虑如何实现内置命令cd、pwd、exit

2.解决思路

​ 考虑实现基本功能,只需要在mysys.c的基础上修改main函数中的内容,原本是直接指定了mysys的参数,本程序需要读取用户输入的命令command,并将command传递到mysys函数中。这里读取屏幕上的内容和输出“>”到屏幕上采用文件读写的方式,标准输入、标准输出的文件描述符分别为0、1。

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;
}

​ 再考虑如何实现内置命令cdpwdexit。内置命令的实现无需创建新进程,因此在mysys函数中调用parse_command分割字符串之后,先进行判断是否为内置命令,若是则调用子函数handle_built_in_command单独处理并返回,若不是则执行mysys中已实现的通过execvp系统调用装入程序并执行,再使用wait系统调用等待子进程结束。

​ 子函数handle_built_in_command中分别处理内置命令cdpwdexit,实现cd需要调用库函数chdir改变当前的工作目录。chdir函数的相关说明如下:

头文件:#include <unistd.h>
定义函数:int chdir(const char * path);
函数说明:chdir()用来将当前的工作目录改变成以参数 path 所指的目录
返回值:执行成功则返回0, 失败返回-1, errno 为错误3.代码

因此将argv[1]作为参数传递,使当前的工作目录改变成以参数 argv[1] 所指的目录,因为有可能存在该目录不存在的情况,故对返回值作判断,若成功则打印当前工作目录,若失败则打印报错信息。

​ 实现pwd需要调用库函数getcwdgetcwd函数的相关说明如下:

头文件:#include <unistd.h>
定义函数:char * getcwd(char * buf, size_t size);
函数说明:getcwd() 会将当前的工作目录绝对路径复制到参数 buf 所指的内存空间,参数 size 为 buf 的空间大小
返回值:执行成功则将结果复制到参数 buf 所指的内存空间, 或是返回自动配置的字符串指针. 失败返回 NULL,错误3.代码存于errno

需要注意的是在调用此函数时,buf 所指的内存空间要足够大。若工作目录绝对路径的字符串长度超过参数size大小,则返回NULLerrno 的值则为ERANGE。但为buf开辟的内存空间过大则造成浪费,可以将参数buf 设为NULLgetcwd()会依参数size的大小自动配置内存(使用malloc()),如果参数 size 也为0,则getcwd()会依工作目录绝对路径的字符串程度来决定所配置的内存大小,进程可以在使用完此字符串后利用free()来释放此空间。在具体实现时采用将参数buf设置为NULL,将参数size设置为0的做法,最后使用free释放空间。

​ 实现exit只需调用exit(0)终止程序即可。

3.代码

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

#define SIZE 256
#define MAX_ARGC 16

struct Command {
  int argc;
  char *argv[MAX_ARGC];
} commands;


// 将 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;

  char *word;
  char *delim = " \n";
  word = strtok(line, delim);
  while(word != NULL) {
    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 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) {
    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 sh1.c -o sh1
$ ./sh1
>echo a b c
>cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
...
>pwd
current working directory: /home/fanyihua/OS
>cd test
current working directory: /home/fanyihua/OS/test
>cd ..
current working directory: /home/fanyihua/OS
>exit
  • 7
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值