Linux(小專案)————shell的實現,包含重定向、內建命令。
阿新 • • 發佈:2018-11-27
bash原理:
通過上面bash的原理我們可以,瞭解到shell的框架與流程:
1.等待使用者輸入命令。
2.解析使用者輸入的字串。
3.建立子程序執行exec程式替換
4.父程序等待子程序退出。
迴圈執行1~4步驟,即可完成my_shell。
最簡單版本的my_shell實現:
#include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<fcntl.h> #include<sys/types.h> char* argv[8]; int argc = 0; //解析使用者指令,字串的切分。 void do_parse(char* buf) { int status = 0; int i; int flag = 0; char* file = 0; if(buf[0] == '\n') { printf("comind error!\n"); return; } for(argc=i=0;buf[i];i++) { if(!isspace(buf[i])&&status == 0) { argv[argc++] = buf+i; status = 1; } else if(isspace(buf[i])) { status = 0; buf[i] = 0; } } argv[argc] = NULL; } //建立子程序,子程序執行exec函式。 void do_exec() { pid_t pid = fork(); if(pid<0) { perror("fork\n"); exit(1); } else if(pid == 0) { execvp(argv[0],argv); perror("execvp!\n"); exit(1); } else { while(1) { if(pid == wait(NULL)) break; } } } int main() { //1.等待使用者輸入命令 char buf[1024] = {}; while(1) { printf("[#fangxia pro_ctrl]"); fflush(stdout); size_t i = read(0,buf,sizeof(buf)-1); buf[i-1] = '\0'; //2.解析使用者 do_parse(buf); //3.建立子程序,子程序函式替換,父程序程等待。 do_exec(); } }
執行效果:
缺點:
1.不能獲取登入名,不能獲取主機名,不能獲取當前目錄。
2.不能操作重定向。
3.不能執行內建指令。
通過這個簡單的shell我們可以在這個基礎上擴充套件很多功能:
- 獲取主機名:
使用到的函式介面:getuid()獲取使用者ID、getpwuid()獲取使用者資訊、gethostname()獲取主機資訊、getcwd()獲取當前目錄路徑。
//獲取主機名 struct passwd* pwd; //uid_t getuid(void) 獲取登入id //struct passwd* getpwuid(uid_t)獲取登入資訊 pwd = getpwuid(getuid()); //獲取主機名 char name[100] = {0}; gethostname(name,sizeof(name)-1); //獲取當前目錄 char cwd[100] = {0}; getcwd(cwd,sizeof(cwd)-1); int len = strlen(cwd); char* p = cwd + len; while(*p != '/'&&len--) { p--; } p++;
- 重定向功能:
方法:
1.在解析出來的使用者命令中查詢重定向符">"。
2.根據重定向符後面的檔案,來重定向檔案描述符。
3.執行exec函式,退出子程序。
重定向原理:
在這裡有一個函式可用來實現重定向:
oldfd檔案描述符指向的內容改為newfd指向的內容(檔案)。
//1.考慮重定向 int i = 0; int flag = 0; int cfd; for(;argv[i];i++) { if(strcmp(">",argv[i]) == 0) { flag = 1; break; } } if(flag) { argv[i] = NULL; close(1); int fd = open(argv[i+1],O_CREAT|O_WRONLY|O_APPEND,0664); //將標準輸出重定向到一個檔案中 cfd = dup2(1,fd); } execvp(argv[0],argv); if(flag)//將標準輸出重定向回1 { close(1); dup2(cfd,1); } exit(0);
- 內建命令的實現:
1.在解析好的字串中查詢,內建指令。
2.在fork之前直接呼叫函式介面實現內建指令。
3.退出子程序。
內建指令cd的實現:
if(strcmp("cd",argv[0]) == 0)
{
if(chdir(argv[1])<0)
{
perror("cd !\n");
exit(0);
}
}
通過這種方法可以實現很多功能,這裡就不一 一列舉了,後面有機會再更新。
my_shell小專案例項:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/types.h>
#include<pwd.h>
#include<string.h>
char* argv[1024];
int argc = 0;
int flag = 0;
int cfd = -1;
void do_parse(char* buf)
{
int status = 0;
int i;
int fd = -1;
char* file = 0;
for(argc=i=0;buf[i];i++)
{
if(!isspace(buf[i])&&status == 0)
{
argv[argc++] = buf+i;
status = 1;
}
else if(isspace(buf[i]))
{
status = 0;
buf[i] = 0;
}
}
argv[argc] = NULL;
}
void do_exec()
{
//2.處理cd
if(strcmp("cd",argv[0]) == 0)
{
if(chdir(argv[1])<0)
{
perror("cd !\n");
exit(0);
}
}
pid_t pid = vfork();
if(pid<0)
{
perror("fork!\n");
exit(0);
}
else if(pid == 0)
{
//3.處理pwd指令
char name[1024] = {0};
if(strcmp("pwd",argv[0]) == 0)
{
if(getcwd(name,sizeof(name)-1)<0)
{
printf("getcwd error!\n");
exit(1);
}
}
//1.考慮重定向
int i = 0;
int flag = 0;
int cfd;
for(;argv[i];i++)
{
//a.檢視有沒有重定向
if(*argv[i]== '>')
{
flag = 1;
//b.判斷是否為追加模式重定向
if(++argv[i] && *argv[i] == '>')
flag = 2;
break;
}
}
//當沒有重定向時刷出pwd結果
if(flag == 0)
{
if(name[0] != '\0')
printf("%s\n",name);
}
//a.覆蓋模式重定向
if(flag == 1)
{
argv[i] = NULL;
close(1);
int fd = open(argv[i+1],O_CREAT|O_WRONLY|O_TRUNC,0664);
//將標準輸出重定向到一個檔案中
cfd = dup2(1,fd);
}
//b.追加模式重定向
if(flag == 2)
{
argv[i] = NULL;
close(1);
int fd = open(argv[i+1],O_CREAT|O_WRONLY|O_APPEND,0664);
//將標準輸出重定向到一個檔案中
cfd = dup2(1,fd);
}
execvp(argv[0],argv);
if(flag)//將標準輸出重定向回1
{
close(1);
dup2(cfd,1);
}
exit(0);
}
else
{
while(1)
{
if(pid == wait(NULL))
break;
}
}
}
int main()
{
//1.等待使用者輸入命令
while(1)
{
char buf[1024] = {};
struct passwd* pwd;
//uid_t getuid(void) 獲取登入id
//struct passwd* getpwuid(uid_t)獲取登入資訊
pwd = getpwuid(getuid());
//獲取主機名
char name[100] = {0};
gethostname(name,sizeof(name)-1);
//獲取當前目錄
char cwd[100] = {0};
getcwd(cwd,sizeof(cwd)-1);
int len = strlen(cwd);
char* p = cwd + len;
while(*p != '/'&&len--)
{
p--;
}
p++;
printf("[%[email protected]",pwd->pw_name);
printf("%s",name);
printf(" %s]$",p);
fflush(stdout);
size_t i = read(0,buf,sizeof(buf)-1);
buf[i-0] = '\0';
//2.解析使用者
do_parse(buf);
//3.建立子程序,子程序函式替換,父程序程等待。
do_exec();
}
}