// makefile
myshell:myshell.c
gcc -o $@ $^ -std=c99
.PHONY:clean
clean:
rm -f myshell
// myshell.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#define LEFT "["
#define RIGHT "]"
#define LABLE "#"
#define LINE_SIZE 1024
#define ARGV_MAX
#define DELIM " "
#define EXIT_CODE 65
int quit = 0;
int last_code = 0;
char PWD[LINE_SIZE];
char myenv[LINE_SIZE];
char command_line[LINE_SIZE];
const char* getusername()
{
return getenv("USER")
}
const char* gethostname()
{
return getenv("HOSTNAME")
}
void getpwd()
{
getcwd(PWD, sizeof(PWD));
}
void Interact(char *command_line, int size)
{
printf(LEFT"%s@%s %s"RIGHT""LABLE" ", getusername(), gethostname(),PWD);
\\ scanf("%s", command_line);此处不能用scanf()因为该函数读到第一个空格就停止了,读不到选项
fgets(command_line, size, stdin);
command_line[strlen(command_line) - 1] = '\0'; \\ 把最后的‘\n’去掉;
return;
}
int splitstring(char* argv[], char *command_line)
{
int i = 0;
argv[i++] = strtok(command_line, DELIM)
while(argv[i++] = strtok(NULL, DELIM));
return i-1;
}
void normalexcute(char* argv)
{
pid_t id = fork();
if(id < 0)
{
perror("fork");
return;
}
if(id == 0)
{
//子进程
execvpe(argv[0], argv, environ);
exit(EXIT_CODE);
}else
{
//父进程等待
int status = 0;
pid_t rid = waitpid(id, &status, 0);
if(rid == id)
{//等待成功了
last_code = WEXITSTATUES(status);
}
}
}
int buildinf(int argc, char* argv)
{
if(argc == 2 && strcmp(argv[0], "cd") == 0)
{
chdir(argv[1]);
getpwd();
sprintf(gentenv("PWD", "%s", PWD));
continue;
return 1;
}
if(strcmp(argv[0], "export") == 0)
{
// putenv(argv[1]);
// 此处有误,在于putenv()只是修改了环境变量的指针,而没有提供存储位置
// 如果argv内存改变,之前putenv()的环境变量也会被改变
strcpy(myenv], argv[1]);
putenv(myenv);
//此处只能有一个环境变量,进一步修改省略;
return 1;
}
if(argc == 2 && strcmp(argv[0], "echo") == 0)
{
if(strcmp(argv[1], "$?") == 0)
{
printf("%d\n", last_code);
last_code = 0;
}
else if(*argv[1] == '$')
{
char* val = getenv(argv[1]+1);
if(val) printf("%s\n", val);
}
else printf("%s\n", argv[1]);
return 1;
}
//特殊处理颜色
if(strcmp(argv[0], "ls") == 0)
{
argv[argc++] = "--color";
argv[argc] = NULL;
}
return 0;
}
main()
{
extern char** environ;
char command_line[LINE_SIZE];
char* argv[LINE_SIZE] = {NULL};
while(quit)
{
Interact(command_line, sizeof(command_line));
int argc = splitstring(argv, command_line);
if(argc == 0) continue;
if(1 == buildinf(argc, argv)) continue;
normalexcute(argv);
}
return 0;
}
#include <stdio.h>
char* fgets(char* s【指向存储位置,存储从文件中读取的信息】, int size【指定最多读取的字符数,包括换行和文件结束标志】, FILE* stream【文件指针,表示要从哪个文件中读取数据】);
—— 成功返回字符串起始地址,失败返回NULL
功能:输入字符或字符串
标准文件流:FILE* stdin、FILE* stdout、FILE* stderr
#include <string.h>
char* strtok(char* str 【指向要分割的字符串的指针。在第一次调用
strtok
时,这个参数是必须的。在后续调用中,如果为NULL
,则strtok
会继续处理上次调用时的字符串。】, const char* delim 【指向分隔符集合的指针。这个字符串中的每个字符都是一个分隔符,用于分割str
字符串】); —— extract tokens from strings
#include <unistd.h>
int chdir(const char* path); —— change working directory
#include <stdio.h>
sprintf(char* str【字符数组的指针,这个数组将存储格式化后的输出字符串】, const char* format, ...【可变参数列表,这些参数根据格式字符串中的格式说明符进行相应的格式化】);
#include <unistd.h> 系统调用,获取当前工作路径
char* getcwd(char* buf 【是一个字符数组,用于存储当前工作目录的路径】, size_t size【
buf
数组的大小】);