源代码(C语言)
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <errno.h>
#include <sys/types.h>
#define SIZE 512
#define ZERO '\0'
#define NUM 32
#define SEP " "
#define SkipPath(p) do{p+=(strlen(p)-1);while(*p!='/') p--;}while(0) //宏函数,获取路径中的最后一个目录
//获取环境变量中的当前用户名
const char* GetUserName() {
const char* name = getenv("USER");
if (name == NULL) return "None";
return name;
}
//获取环境变量中的主机名
const char* GetHostName() {
const char* name = getenv("HOSTNAME");
if (name == NULL) return "None";
return name;
}
//获取环境变量中的当前路径
const char* GetCwd() {
const char* cwd = getenv("PWD");
if (cwd == NULL) return "None";
return cwd;
}
//输出命令行提示符
void CommandLine() {
char line[SIZE]; //自定义缓冲区
const char* username = GetUserName();
const char* hostname = GetHostName();
const char* cwd = GetCwd();
SkipPath(cwd);
snprintf(line, sizeof(line), "[%s@%s %s]> ", username, hostname, strlen(cwd) == 1 ? "/" : cwd + 1); //命令行提示符中的路径改为相对路径
printf("%s", line);
fflush(stdout);
}
//获取用户输入命令
int GetCommand(char command[], size_t n) {
char* s = fgets(command, n, stdin); //用户输入命令到command中
if (s == NULL) return -1;
command[strlen(command) - 1] = ZERO;
return strlen(command);
}
char* gArgv[NUM]; //接收分割后的命令
//分割一行命令
void SplitCommand(char command[], size_t n) {
(void)n;
gArgv[0] = strtok(command, SEP);
int index = 1;
while ((gArgv[index++] = strtok(NULL, SEP)));
}
//执行命令
int lastcode = 0;
void ExecuteCommand() {
pid_t id = fork(); //创建子进程
if (id < 0) {
printf("fork failed\n");
exit(-1);
} else if (id == 0) { //子进程
execvp(gArgv[0], gArgv);
exit(errno);
} else { //父进程
int status = 0;
pid_t rid = waitpid(id, &status, 0);
if (rid > 0) { //等待成功
lastcode = WEXITSTATUS(status);
if (lastcode != 0) //进程运行结果不正确
printf("%s:%s:%d\n", gArgv[0], strerror(lastcode), lastcode);
}
}
}
//获取家目录路径
const char* GetHome() {
const char* home = getenv("HOME");
if (home == NULL) return "/";
return home;
}
char cwd[SIZE * 2];
//检查是否为内建命令
int CheckBuildIN() {
int yes = 0;
const char* cmd = gArgv[0];
if (strcmp(cmd, "cd") == 0) {
yes = 1;
const char* path = gArgv[1];
if (path == NULL) path = GetHome(); //特殊判断,直接执行cd命令会进入家目录
chdir(path); //切换路径到path路径(path可能是..或者一串路径)
char temp[SIZE * 2];
getcwd(temp, sizeof(temp)); //获取最新当前路径,存放在temp中
snprintf(cwd, sizeof(cwd), "PWD=%s", temp); //将最新的工作路径按"PWD=%s"格式写入cwd中
putenv(cwd); //更新父进程环境变量,这样命令行提示符中的路径才会变化
}
return yes;
}
int main() {
int quit = 0;
while (!quit) {
// 输出命令行提示符
CommandLine();
// 获取用户输入命令
char command[SIZE];
int n = GetCommand(command, sizeof(command));
if (n <= 0) return -1; // 用户没有输入命令
// 分割获取到的一行命令
SplitCommand(command, sizeof(command));
//检查是否为内建命令
n = CheckBuildIN();
if (n) continue; //如果是内建命令,就让父进程亲自执行,不创建子进程
// 执行命令
ExecuteCommand();
}
}