Topic:
Unix外壳和历史特征
Requirements:
Part1 创建子进程
- 拆分用户输入为多个标记,并将这些标记存到字符串args[]数组中。
- 用fork()创建一个子进程
- 子进程调用execvp(char*command,char *params[])函数执行用户指定命令
- 检查数组最后一个位置是否有&,以便确定是否等待子进程
Part2 创建历史功能
1、允许用户访问最近输入的命令,通过这个功能用户最多可以访问10个命令,在osh>后面输入history,用户能够累出历史命令
2、当用户输入!!时,将会执行最近的历史命令,如果没有历史命令,应当产生消息“No commands in history”
3、当用户输入!和一个整数n之后,将执行第N个历史命令,如果没有历史命令,应当产生消息“No such command in history”
Procedure:
1、先要实现将输入的命令分割,这里用args(char* args[])数组来存放分割后的标志,例如ls –al, 每次输入下一个命令之前数组的内容清空,存储形式如下:
args[0]=ls
args[1]=-al
do {
char* s = (char*)malloc(128);
scanf("%s", s);//输入空格或者回车换行结束
c = getchar();
args[i] = s;
h[num % 10].args[i++] = s;
if (strcmp(args[0], "exit") == 0) { should_run = 0; }//用户在提示符后面输入exit后,should_run为0并且终止;
if (strcmp(args[i - 1], "&") == 0) { i--; } //输入& 显示waitpid:success
} while (c == ' '); //如果是空格继续循环
args[i] = NULL; h[num%10].args[i] = NULL;//数组的结束位置
2、调用fork()函数创建子进程,并将args[]数组内的命令给execvp(args[0], args)函数
pid_t id = fork(); //创建子进程,父进程和子进程之间共享代码段(建立相同的程序副本)
if (id < 0) {
perror("fork"); //抛出异常
}
if (id == 0) {
execvp(args[0], args); //exec系列的一种,用于运行新程序
exit(1); //调用exit()来终止程序
}
else {
int status = 0;
pid_t ret = waitpid(id, &status, 0);//等待子进程结束
if (ret > 0 && WIFEXITED(status))
{
}
else { perror("waitpid"); }
}
fork()与exec的使用和区别:
系统调用fork():创建子进程,父进程和子进程之间共享代码段(建立相同的程序副本),仅通过复制指令、用户数据和系统数据段来创建从现存进程克隆的新进程,该新进程不是从程序初始化得来的,所以旧进程和新进程执行同样的指令。
系统调用exec():是以新的进程去代替原来的进程,但进程的PID保持不变。也就是exec系统调用并没有创建新的进程,只是替换了原来进程上下文的内容。原进程的代码段,数据段,堆栈段被新的进程所代替。
一般使用方法为:先调用fork(),再使用exec()来达到提高效率的目的。
exit():终止进程,如写如exit(1)来结束进程。
系统调用wait():等待子进程的终止。
3、创建一个结构体数组h[10],用以存放历史的十个命令
struct History
{
char* args[MAX_LINE/2+1];
}h;
4、每次都将输入的有效命令存入结构体数组h中(数组h是一个循环数组,存满最后一个存储单元后,又从第一个存储单元开始存放,将之前的存储内容覆盖)
5、用户输入的命令存入数组args[]和h[]后,判断当前输入命令的第一个字符串是否是history、!!或者!+N,若满足这三个条件,执行以下命令:
if (strcmp(args[0], "history") == 0) {
int count2=0;
if (!h[count].args[0]) { printf("No commands in history"); }
while (h[count].args[count2])
{
while(h[count].args[count2])
{
printf("%d %s ", count, h[count].args[count2++]);
}printf("\n");
count2=0;count++;
}
}
if (strcmp(args[0],"!!") == 0) {
if (!h[num - 1].args[0]) { printf("No such command in history"); }
else {
process(num-1); }
}
assume(args); //函数assume实现!加数字执行第N条历史记录的功能
/**
* Simple shell interface program.
*
* Operating System Concepts - Ninth Edition
* Copyright John Wiley & Sons - 2013
*/
#include <stdio.h>
#include "unistd.h"
#include<string.h>
#include<stdlib.h>
#include<sys/types.h> /* 提供类型pid_t的定义 */
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>/*pid_t wait(int *status)*/
#include <pwd.h>
#define MAX_LINE 80 /* 80 chars per line, per command */
char* args[MAX_LINE / 2 + 1]; /* command line (of 80) has max of 40 arguments */
int should_run = 1;
struct History
{
char* args[MAX_LINE/2+1];
};
History h[10]; int num = 1;
int assume(char *a[]);
void process(int i);
void play(int i)
{
int count2=0;
while(h[i].args[count2++]){
printf("%s ", h[i].args[count2++]);
}printf("\n")
}
int main(void)
{
while (should_run) {
printf("osh>");//设置提示符为OSH
fflush(stdout);
int i = 0, count = 1;
char c;
do {
char* s = (char*)malloc(128);
scanf("%s", s);//输入空格或者回车换行结束
c = getchar();
args[i] = s;
h[num % 10].args[i++] = s;
if (strcmp(args[0], "exit") == 0) { should_run = 0; }//用户在提示符后面输入exit后,should_run为0并且终止;
if (strcmp(args[i - 1], "&") == 0) { i--; } //输入& 显示waitpid:success
} while (c == ' '); //如果是空格继续循环
args[i] = NULL; h[num%10].args[i] = NULL;//数组的结束位置
if (strcmp(args[0], "history") == 0) {
int count2=0;
if (!h[count].args[0]) { printf("No commands in history"); }
while (h[count].args[count2])
{
while(h[count].args[count2])
{
printf("%d %s ", count, h[count].args[count2++]);
}printf("\n");
count2=0;count++;
}
}
if (strcmp(args[0],"!!") == 0) {
if (!h[num - 1].args[0]) { printf("No such command in history"); }
else {
play(num-1);
//pid_t id = fork();execvp(h[num - 1].args[0], h[num - 1].args);
process(num); }
}
assume(args);
process(num);
num++;
}
return 0;
}
void process(int i)
{
pid_t id = fork(); //创建子进程,父进程和子进程之间共享代码段(建立相同的程序副本)
if (id < 0) {
perror("fork"); //抛出异常
}
if (id == 0) {
execvp(h[i].args[0],h[i]args); //exec系列的一种,用于运行新程序
exit(1); //调用exit()来终止程序
}
else {
int status = 0;
pid_t ret = waitpid(id, &status, 0);//等待子进程结束
if (ret > 0 && WIFEXITED(status))
{
}
else { perror("waitpid"); }
}
}
void row(int i)
{
if (!h[i].args[0]) { printf("No such command in history"); }
else{play(1);//id = fork();execvp(h[1].args[0], h[1].args); exit(1);
process(i);
}
}
int assume(char *args[])
{
pid_t id;
if (strcmp(args[0],"!1")==0) {
row(1);
}
if (strcmp(args[0],"!2")==0) {
row(2);
//if (!h[2].args[0]) { printf("No such command in history"); }
//else{play(2);id = fork();execvp(h[2].args[0], h[2].args); exit(1);}
}
if (strcmp(args[0],"!3")==0) {
row(3);
//if (!h[3].args[0]) { printf("No such command in history"); }
//else{play(3);id = fork();execvp(h[3].args[0], h[3].args); exit(1);}
}
if (strcmp(args[0],"!4")==0) {
row(4);
//if (!h[4].args[0]) { printf("No such command in history"); }
//else{play(4);id = fork();execvp(h[4].args[0], h[4].args);exit(1);}
}
if (strcmp(args[0],"!5")==0) {
row(5);
//if (!h[5].args[0]) { printf("No such command in history"); }
//else{play(5);id = fork();execvp(h[5].args[0], h[5].args); exit(1);}
}
if (strcmp(args[0],"!6")==0) {
row(6);
//if (!h[6].args[0]) { printf("No such command in history"); }
//else{play(6);id = fork();execvp(h[6].args[0], h[6].args); exit(1);}
}
if (strcmp(args[0],"!7")==0) {
row(7);
//if (!h[7].args[0]) { printf("No such command in history"); }
//else{play(7);id = fork();execvp(h[7].args[0], h[7].args); exit(1);}
}
if (strcmp(args[0],"!8")==0) {
row(8);
//if (!h[8].args[0]) { printf("No such command in history"); }
//else{play(8);id = fork();execvp(h[8].args[0], h[8].args); exit(1);}
}
if (strcmp(args[0],"!9")==0) {
row(9);
//if (!h[9].args[0]) { printf("No such command in history"); }
//else{play(9);id = fork();execvp(h[9].args[0], h[9].args); exit(1);}
}
if (strcmp(args[0],"!10")==0) {
row(0);
//if (!h[0].args[0]) { printf("No such command in history"); }
//else{play(0);id = fork();execvp(h[0].args[0], h[0].args); exit(1);}
}
return 0;
}