1 Star 0 Fork 0

YoungMLet/Linux_Study

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
myshell.c 5.56 KB
一键复制 编辑 原始数据 按行查看 历史
YoungMLet 提交于 2024-01-14 00:39 . 重定向
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <ctype.h>
#define SEP " "
#define SIZE 64
#define NUM 1024
#define NoneRedir 0
#define OutputRedir 1
#define AppendRedir 2
#define InputRedir 3
int lastcode = 0;
char cwd[1024];
char* filename = NULL;
int redir = NoneRedir;
const char* getUserName()
{
const char* name = getenv("USER");
if(name) return name;
else return "none";
}
const char* getHostName()
{
const char* hostname = getenv("HOSTNAME");
if(hostname) return hostname;
else return "none";
}
const char* getCwd()
{
const char* cwd = getenv("PWD");
if(cwd) return cwd;
else return "none";
}
int getUserCommand(char* command, int num)
{
printf("[%s@%s %s]# ", getUserName(), getHostName(), getCwd());
char* input = fgets(command, num, stdin); // 最后输入的是 \n
if(input == NULL) return -1;
command[strlen(command) - 1] = '\0';
return strlen(command);
}
void CommandSplit(char* in, char* out[])
{
int argc = 0;
out[argc++] = strtok(in, SEP);
// 截取以空格为分隔符的字符串放入out中,因为 in 字符最后以NULL结尾,所以当没截取到NULL时循环继续
// 其中 strtok 为截取字符串的库函数,第一个参数为需要截取的字符串,当设为NULL时,会继续扫描上一次成功调用函数的位置
while(out[argc++] = strtok(NULL, SEP));
}
int excute(char* argv[])
{
pid_t id = fork();
if(id < 0) return -1;
else if(id == 0)
{
// child
// exec command
if(redir == InputRedir)
{
int fd = open(filename, O_RDONLY);
dup2(fd, 0);
}
else if(redir == OutputRedir)
{
int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
dup2(fd, 1);
}
else if(redir == AppendRedir)
{
int fd = open(filename, O_WRONLY | O_CREAT | O_APPEND, 0666);
dup2(fd, 1);
}
else
{
// nothing to do
}
execvp(argv[0], argv);
exit(1);
}
else
{
// father
int status = 0;
pid_t rid = waitpid(id, &status, 0);
// 获取退出码
if(rid > 0)
{
lastcode = WEXITSTATUS(status);
}
}
return 0;
}
void cd(const char* path)
{
// chdir---更改工作路径的接口,谁调就更改谁的工作路径
chdir(path);
char tmp[1024];
// 获取进程当前所在的绝对工作路径
getcwd(tmp, sizeof tmp);
sprintf(cwd, "PWD=%s", tmp);
putenv(cwd);
}
// 重新理解内建命令,它其实就是 bash 自己执行的,类似与自己内部的一个函数!
// 返回1代表是内建命令,0表示不是内建命令
int isBuildin(char* argv[])
{
if(strcmp("cd", argv[0]) == 0)
{
char* path = NULL;
if(argv[1] == NULL) path = ".";
else path = argv[1];
cd(path);
return 1;
}
else if(strcmp("echo", argv[0]) == 0)
{
if(argv[1] == NULL)
{
printf("\n");
return 1;
}
if(*(argv[1]) == '$' && strlen(argv[1]) > 1)
{
char* val = argv[1] + 1;
if(strcmp(val, "?") == 0)
{
printf("%d\n", lastcode);
lastcode = 0;
}
else
{
const char* enval = getenv(val);
if(enval) printf("%s\n", enval);
else printf("\n");
}
return 1;
}
else
{
printf("%s\n", argv[1]);
return 1;
}
return 0;
}
// else if(0) {}
// ...
return 0;
}
#define SkipSpace(pos) do {while(isspace(*pos)) pos++;} while(0)
void CheckRedir(char* usercommand, int len)
{
char* end = usercommand + len - 1;
char* start = usercommand;
while(end > start)
{
if(*end == '>')
{
// 追加重定向
if(*(end - 1) == '>')
{
*(end - 1) = '\0';
filename = end + 1;
SkipSpace(filename);
redir = AppendRedir;
break;
}
// 输出重定向
else
{
*end = '\0';
filename = end + 1;
SkipSpace(filename);
redir = OutputRedir;
break;
}
}
// 输入重定向
else if(*end == '<')
{
*end = '\0';
filename = end + 1;
SkipSpace(filename);
redir = InputRedir;
break;
}
else
{
end--;
}
}
}
int main()
{
char usercommand[NUM];
char* argv[SIZE];
while(1)
{
redir = NoneRedir;
filename = NULL;
// 1、打印提示符并且获取用户命令字符串长度,如果小于等于0,没有意义,继续重新获取
int n = getUserCommand(usercommand, sizeof usercommand);
if(n <= 0) continue;
// 判断是否具有重定向功能
CheckRedir(usercommand, strlen(usercommand));
// 2、分割字符串 --- "ls -a -l" -> "ls" "-a" "-l"
CommandSplit(usercommand, argv);
// 3、判断并执行内建命令
n = isBuildin(argv);
if(n) continue;
// 4、执行对应的命令
excute(argv);
}
return 0;
}
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
C++
1
https://gitee.com/YoungMLet/Linux_Study.git
git@gitee.com:YoungMLet/Linux_Study.git
YoungMLet
Linux_Study
Linux_Study
master

搜索帮助