首先Shell的主逻辑为:
while (1)
{
type_prompt();
read_command();
if (fork() != 0) {
waitpid();
}
else {
execve();
}
}
即如下一个循环:
- 输出提示符,
- 等待输入命令,
- 创建子进程,
- 执行命令。
下面一一介绍实现。
输出提示符
type_prompt()采取“用户名@主机名:目录+prompt”的形式,且如果为root用户,prompt为#,普通用户为$。
- getpwuid(getuid())获取用户信息;
- gethostname()获取主机名;
- getcwd()获取当前目录;
- geteuid()判断是否为root用户。
等待输入命令
首先使用strtok()函数判断命令分为几部分(按照空格分开),然后创建一个char*数组保存命令并返回。
创建子进程
使用fork()函数创建子进程。
执行命令
使用execve()函数执行命令。
Linux环境下的编译和运行如下图
全部源代码
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include<sys/wait.h>
#include <fcntl.h>
#include <unistd.h>
#include <pwd.h>
#include <string.h>
void type_prompt() {
char hostname[256] = { '\0' };
gethostname(hostname, sizeof(hostname));
char curpath[256] = { '\0' };
getcwd(curpath, 256);
char prompt = '$';
if (geteuid() == 0) {
prompt = '#';
}
printf("%s@%s:%s%c", getpwuid(getuid())->pw_name, hostname, curpath, prompt);
}
char** read_command() {
char cmd[256];
fgets(cmd, 256, stdin);
const char *delim = " ";
int num = 0;
char copy[256];
strcpy(copy, cmd);
char *p = strtok(copy, delim);
while (p != NULL) {
p = strtok(NULL, delim);
num++;
}
char **argv = (char**)malloc((num+1) * sizeof(char*));
for (int i = 0; i < num; i++) {
argv[i] = (char*)malloc(256 * sizeof(char));
}
num = 0;
strcpy(copy, cmd);
p = strtok(copy, delim);
while (p != NULL) {
strcpy(argv[num], p);
p = strtok(NULL, delim);
num++;
}
argv[num] = (char *)0;
return argv;
}
int main()
{
while (1)
{
type_prompt();
char* envp[] = { "PATH=/bin",0 };
char** argv= read_command();
if (fork() != 0) {
waitpid(-1, NULL, 0);
}
else {
execve("/bin/ls", argv, envp);
}
}
return 0;
}