真正了不起的程序员对自己程序的每一个字节都了如指掌。
计算机科学领域中,没有什么问题是增加一个中间层解决不了的。
Ubuntu20.04 64位虚拟机
如何做一个类似的 shell 解释器,按照不用重复造轮子的思想,所以最直接的思路是:编写接口利用现成的 bash 。这是主要的思想。
1,需要做一个交互界面,能够仿照正常的终端格式打印出提示符
2,需要处理用户的输入指令,能够对用户的命令进行处理
1,包含于头文件
#include
struct passwd* a = getpwuid(getuid());//获得提示符
char name[32];
gethostname(name, 31);
char current_dir[128];
getcwd(current_dir, 127);
...
//a 是一个结构体,在此只需要访问用户名这一个成员。
printf("%s@%s:%s$ ",a->pw_name,name,current_dir);//打印提示符
2,每当有用户进行输入时,采用 fork()创建一个子进程,让子进程来执行具体的用户输入,执行完毕用 wait() 来回收子进程。
执行用户输入时,在此采用 exec 函数族中的 execvp(), 这个函数原型如下:
int execvp(const char *file, char *const argv[]);
关于返回值:该函数执行失败则直接返回-1。
第一个参数是要运行的脚本文件,会在环境变量PATH中查找并执行,也就是说它可以直接对用户输入进行处理(比如 ls 命令),不需要进一步地转化(将 ls 转化为 /bin/ls ),用起来较为方便。
第二个参数是一个字符串数组,它是一个参数列表,下标为0,1,2…。注意:其中下标为0的参数是待执行的命令本身,也就是脚本文件名。
还需要注意字符串数组最后一个参数必须为 NULL。
举例如下:
当用户输入命令 ls -l 时,我们所需要做的工作是:假设字符串数组名为 argv,令 argv[0]=“ls” ,令 argv[1]="-l" ,令 argv[2]=NULL 。然后采用函数调用方法为 execvp(argv[0],argv) 来执行 ls -l 命令。
printf("my pid is %d,I will execute the task.\n\n", getpid());
if (execvp(arg[0], arg) < 0)//执行用户命令(如ls命令:exec函数中不带p的需要采用/bin/ls形式,而带p的会自动查找环境变量。)
{
printf("error in execv().\n");
exit(-1);
}
else
exit(0);
#include
#include
#include
#include
#include
#include
#include
#define SIZE 512
#define LEN 32
void getInput(char* arg[],char* input);
int main()
{
printf("hello,It's me.\n");
char* input=(char*)malloc(SIZE);//存储用户输入的命令
char* arg[10];//参数列表 1+9
char exit_p[] = "exit"; //退出处理
char exit_input[5];
struct passwd* a = getpwuid(getuid());//获得提示符
char name[32];
gethostname(name, 31);
char current_dir[128];
getcwd(current_dir, 127);
while (1)
{
printf("%s@%s:%s$ ",a->pw_name,name,current_dir);//打印提示符
memset(input, 0, SIZE);//每次都清0
fgets(input,SIZE-1,stdin);//获得用户输入
memcpy(exit_input, input, 4);//进行退出判断,是否为"exit\n"
if(input[4]=='\n')
{
exit_input[4]='\0';
if (strcmp(exit_p, exit_input) == 0)
break; //用户输入exit进行退出
}
getInput(arg, input);//将输入字符串转换为arg参数数组
pid_t pid = fork();
if (pid < 0)
printf("error in fork().\n");
else if (pid == 0)
{
printf("my pid is %d,I will execute the task.\n\n", getpid());
if (execvp(arg[0], arg) < 0)//执行用户命令(如ls命令:不带p的需要采用/bin/ls形式,带p的会自动查找环境变量。)
{
printf("error in execv().\n");
exit(-1);
}
else
exit(0);
}
else
{
int status;
int re = wait(&status);
printf("\nchild process quit with status%d.\n", status);
printf("child process pid=%d.\n", re);
for (int i = 0; i < 10; i++)
if (arg[i] != NULL)
free(arg[i]);//每次都要释放内存。
}
}
free(input);
return 0;
}
void getInput(char* arg[10], char* input)
{
for (int i = 0; i < 10; i++)
arg[i] = NULL; //保证最后一个参数始终为NULL
if (input[0] == '\n')
return;
int i = 0,flag = 1;//i 作为input的下标扫描输入字符. flag 用于多个空格符的检测以及是否使用 malloc()
int j,k = -1;//k 作为参数的下标,最大为9. j为每个参数中字符的下标,最大为31
while (input[i] != '\n')
{
if (input[i] == ' ')
flag = 1;
else
{
if(flag)
{
flag = 0;
k++;
arg[k] = (char*)malloc(LEN);
memset(arg[k], 0, LEN); //保证每个可用参数最后为'\0'
j = 0;
}
arg[k][j++] = input[i];
}
i++;
}
return;
}
能够实现 ls,ps,touch,rm等命令,不能实现 cd 命令,只能在当前目录下进行操作。
输入 exit 退出。