shell主要实现了管道,重定向输入输出,后台运行,历史命令查询,cd,自动补全
简单历史命令查询主要用到的readline库中history中add_history函数,同时实现了上下键查找历史命令
同样TAB的自动补全命令也是通过readline()函数来实现输入重定向:把<右边的文件的内容输入到<左边的命令中。
输出重定向:把运行>左边命令得出的结果输入到>右边的文件中。
(若以>输出到一个已经存在的文件中,那么文件就会被覆盖掉,如果想追加内容而不想删除原来文件的内容则可以用’>>’)
管道命令:a|b,a和b分别是两个命令,执行a命令后得到的结果可以被b接受,b命令继续对结果进行相应的处理。
(输入重定向)
if(pid == 0)
{
if(!(find_command(arg[0])))
{
printf("%s:command not found",arg[i]);
exit(0);
}
fd=open(file,O_RDONLY);
dup2(fd,0);
execvp(arg[0],arg);
exit(0);
}
(输出重定向)
if(pid == 0)
{
if(!(find_command(arg[0])))
{
printf("%s:command not found",arg[i]);
exit(0);
}
、 fd=open(file,O_RDONLY);
dup2(fd,0);
execvp(arg[0],arg);
exit(0);
}
输入重定向和输出重定向
输入重定向和输出重定向的方法很相似,主要都是运用了dup2()这个函数:
dup2( int oldfd , int newfd )
dup2可以用参数newfd指定新文件描述符的数值,dup2调用成功,返回新的描述符。
(文件描述符0代表标准输入文件,一般就是键盘; 文件描述符1代表标准输出文件,一般是显示器; 文件描述符2代表标准错误输出,一般也指显示器 )
dup2(fd,1)的意思就是把文件描述符替换为1,因为执行命令得到的结果是会向名为1的输出流中输出得到的结果,现在将文件描述符的数值变为1,执行命令后自然就会将结果输入到文件中了。
dup2(fd,0)同理,将文件符的值变为0,命令会从名为0的输入流中读取信息。
- 其实管道命令的实现方法也很简单,也运用了输入输出重定向
实现的方法就是将|左边的命令执行的结果保存至文件中,在运行|右边的命令时将再将刚刚文件中的内容传入|右边的命令去执行。
(管道命令)
if(pid == 0)
{
int pid2;
int status2;
int fd2;
if((pid2 =fork()) < 0)
{
printf("fork2 error\n");
return;
}
else if(pid2 == 0)
{
if(!(find_command(arg[0])))
{
printf("%s :command not found\n",arg[0]);
exit(0);
}
fd2 = open("/tmp/youdonotknowfile",O_WRONLY|O_CREAT|O_TRUNC,0644); //创建临时文件保存管道符前面的操作
dup2(fd2,1);
execvp(arg[0],arg);
exit(0);
}
、 if(waitpid(pid2,&status2,0) == -1) //确保管道符前的操作先执行完成
printf("please wait process error\n");
if(!(find_command(arg[i])))
{
printf("%s :command not found\n",arg[0]);
exit(0);
}
fd2 = open("/tmp/youdonotknowfile",O_WRONLY|O_CREAT|O_TRUNC,0644);
dup2(fd2,0);
execvp(argnext[0],argnext);
if(remove("/tmp/youdonotknowfile"))
printf("remove error\n");
exit(0);
}
code of myshell
main.c
/*************************************************************************
> File Name: main.c
> Author: YinJianxiang
> Mail: YinJianxiang123@gmail.com
> Created Time: 2017年07月26日 星期三 11时48分54秒
************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <readline/readline.h>
#include <readline/history.h>
#include "./myhead.h"
#include "./func.h"
int main(int argc, char *argv[]) {
int i;
int argcount = 0;
char arglist[100][256];
char **arg = NULL;
char *buf = NULL;
char histroy[100][256];
int his_count;
char temp[256];
buf = (char *)malloc(256);
if(buf == NULL) {
perror("malloc faid");
exit(-1);
}
while(1) {
signal(SIGINT,SIG_IGN);
signal(SIGQUIT,SIG_IGN);
signal(SIGSTOP,SIG_IGN);
signal(SIGTSTP,SIG_IGN);
memset(buf,0,256);
//print_prompt(); //显示当前路径
get_input(buf,&his_count,histroy); //接受命令
/*exit或者logout退出*/
if(strcmp(buf,"exit") == 0 || strcmp(buf,"logout") == 0) {
break;
}
for(i = 0;i < 100;i++) {
arglist[i][0] = 0; //初始化命令
}
argcount = 0;
explain_input(buf,&argcount,arglist); //解析命令
do_cmd(argcount,arglist,his_count,histroy); //执行命令
}
getcwd(temp,100);
strcmp(last_dir,temp);
if(buf != NULL) { //释放空间
free(buf);
buf = NULL;
}
return 0;
}
myhead.h
/*************************************************************************
> File Name: myhead.h
> Author: YinJianxiang
> Mail: YinJianxiang123@gmail.com
> Created Time: 2017年07月26日 星期三 11时10分12秒
************************************************************************/
#ifndef _MYHEAD_H
#define _MYHEAD_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <dirent.h>
#define normal 0
#define out_redirect 1
#define double_out_redirect 2
#define in_redirect 3
#define have_pipe 4
#define is_background 1
#define not_background 0
void print_prompt(); //打印开始时的所在路径
void get_input(char *buf,int *his_count,char history[100][256]); //获得用户输出
void explain_input(char *buf,int *argcount,char arglist[100][256]); //接受输入
void do_cmd(int argcount,char arglist[100][256],int his_count,char history[100][256]); //执行命令
char last_dir[256];
#endif
func.h
/*************************************************************************
> File Name: func.h
> Author: YinJianxiang
> Mail: YinJianxiang123@gmail.com
> Created Time: 2017年07月26日 星期三 11时48分40秒
************************************************************************/
#ifndef _FUNC_H
#define _FUNC_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <dirent.h>
#include "./myhead.h"
static int find_command(char *command); //查找命令是否存在
//得到输入,统计历史命令个数并存储
void get_input(char *buf,int *historycount,char history[100][256]) {
int count = 0;
int i;
int len;
char *temp;
char *msg;
char str[256];
msg = (char *)malloc(200);
getcwd(msg,100);
temp = (char* )malloc(sizeof(char) * 256);
memset(temp,0,sizeof(temp));
sprintf(str,"myshell$$ \033[;34m%s/\033[0m",msg);
//简单处理界面,获得当前目录
temp = readline(str);
strcpy(buf, temp);
//readline会打印里面的字符串,然后接受所输入的字符串
len = strlen(buf);
for(i = 0;i < len; i++) {
if(buf[i] == ' '|| buf[i] == '\n')
count++;
}
//统计空格和换行
//如果直接空格或换行不存储命令
if(count != len) {
if(*historycount == 0){
strcpy(history[*historycount],buf);
*historycount = *historycount + 1;
add_history(buf);
} else {
//如果前后两次命令相同,不再存储
if(strcmp(history[*historycount-1],buf) != 0){
strcpy(history[*historycount],buf);
*historycount = *historycount + 1;
add_history(buf);//将命令存储在历史列表,可以用上下键来进行操作
}
}
}
free(temp);
}
//解释输入,将输入以空格为界线分开,argcount存储命令个数,arglist存储具体命令
void explain_input(char *buf,int *argcount,char arglist[100][256]) {
int number = 0 ;
char *p = buf;
char *q = buf;
while(1){
if(p[0] == '\n' || p[0] == 0) //空格或者换行结束
break;
if(p[0] == ' ') //跳过空格
p++;
else{
q = p;
number = 0;
while( (q[0] != ' ') && q[0] != 0 && q[0] != '\n'){
number++; //代表单个命令的长度
q++; //移动到命令结尾
}
strncpy(arglist[*argcount],p,number+1); //把单个命令解析出来
arglist[*argcount][number] = 0; //最后加上结尾
*argcount = *argcount + 1; //移动到下一个字符串
p = q; //将p移动到末尾位置
}
}
}
//将命令分类,分别执行
void do_cmd(int argcount,char arglist[100][256],int historycount,char history[100][256]) {
int flag = 0;
int how = normal;
int backgroud = not_background;
int status;
int i;
int fd;
char *arg[argcount + 4];
char *argnext[argcount + 4];
char *file;
pid_t pid;
for(i = 0;i < argcount;i++)
arg[i] = (char *)arglist[i];
arg[argcount] = NULL;
//解析是否是后台运行
for(i=0;i < argcount;i++){
if(strncmp(arg[i],"&",1) == 0){
if(i == argcount - 1){
backgroud = 1;
arg[argcount - 1] =NULL;
break;
} else {
printf("wrong command\n");
return ;
}
}
}
for(i=0 ;arg[i] != NULL; i++){
//解析重定向输入
if(strcmp(arg[i],">") == 0){
flag++;
how = out_redirect;
if (arg[i+1] == NULL)
flag++;
}
//解析追加重定向输入
if(strcmp(arg[i],">>") == 0) {
flag++;
how = double_out_redirect;
if (arg[i+1] == NULL) {
flag++;
}
}
//解析重定向输出
if( strcmp(arg[i],"<") == 0){
flag++;
how = in_redirect;
if(i == 0)
flag++;
}
//解析管道命令
if( strcmp(arg[i],"|") == 0){
flag++;
how = have_pipe;
if(arg[i+1] == NULL)
flag++;
if( i == 0)
flag++;
}
}
//只允许单个 > < >> |命令
if(flag > 1){
printf("wrong command !\n");
return ;
}
if( how == out_redirect){
for(i = 0;arg[i] != NULL;i++){
if(strcmp(arg[i],">") == 0){
file = arg[i+1];
arg[i] = NULL;
}
}
}
if(how == double_out_redirect) {
for(i = 0;arg[i] != NULL;i++ ) {
if(strcmp(arg[i],">>") == 0) {
file = arg[i+1];
arg[i] = NULL;
}
}
}
if(how == in_redirect){
for(i=0; arg[i] != NULL;i++){
if(strcmp(arg[i],"<") == 0){
file = arg[i+1];
arg[i] = NULL;
}
}
}
if(how == have_pipe){
for( i = 0; arg[i] != NULL ;i++){
if(strcmp(arg[i],"|") == 0){
arg[i] = NULL;
int j;
for(j = i+1;arg[j] != NULL;j++){
argnext[j-i-1] = arg[j];
}
argnext[j-i-1] = arg[j];
break;
}
}
}
// 如果命令为history 则输出历史。
if(strcmp(arg[0],"history") == 0&& argcount == 1){
for(i = 0;i < historycount;i++){
printf("%d\t %s\n",i+1,history[i]);
}
return;
}
//完成内嵌cd命令
if(strcmp(arg[0],"cd") == 0 && argcount == 2){
//处理特殊的cd或cd ~命令
if(argcount == 1 || argcount == 2 && strcmp(arg[1],"~") == 0) {
chdir("/home/yinjianxiang");
return;
}
if(chdir(arg[1]) == -1){
perror("chdir");
}
return;
}
//fork一个子进程,执行参数
if( (pid = fork()) < 0){
printf("fork error\n");
return ;
}
switch(how){
case normal:
if(pid == 0){
if( !(find_command(arg[0])) ){
printf("%s :command not found !\n",arg[0]);
exit(0);
}
execvp(arg[0],arg); //加载参数命令
exit(0);
}
break;
// >
case out_redirect:
if(pid == 0){
if( !(find_command(arg[0])) ){
printf("%s :command not found !\n",arg[0]);
exit(0);
}
fd = open(file,O_RDWR | O_CREAT | O_TRUNC,0644);//
dup2(fd,1);
execvp(arg[0],arg);//加载参数命令
exit(0);
}
break;
// >
case double_out_redirect:
if(pid == 0){
if( !(find_command(arg[0])) ){
printf("%s :command not found !\n",arg[0]);
exit(0);
}
fd = open(file,O_RDWR | O_CREAT | O_TRUNC |O_APPEND,0644);//打开文件
dup2(fd,1); //把输出从屏幕交给了文件
execvp(arg[0],arg);
close(fd);
exit(0);
}
break;
// <
case in_redirect:
if(pid == 0){
if( !(find_command(arg[0])) ){
printf("%s :command not found !\n",arg[0]);
exit(0);
}
fd = open(file,O_RDONLY); //打开文件
dup2(fd,0); //将输入从键盘交给了文件
execvp(arg[0],arg);
close(fd);
exit(0);
}
break;
// |
case have_pipe:
if(pid == 0){
int pid2;
int status2;
int fd2;
//fork执行|前面的命令
if( (pid2 = fork()) < 0){
printf("fork2 error\n");
return ;
}
else if(pid2 == 0){
if(!(find_command)(arg[0])){
printf("%s :command not found !\n",arg[0]);
exit(0);
}
//将屏幕的输出流入文件之中
if((fd2 = open("/tmp/tempfile",O_WRONLY | O_CREAT | O_TRUNC ,0644)) == -1) {
printf("open /tmp/tempfile failed!");
}
dup2(fd2,1);
execvp(arg[0],arg);
close(fd2);
exit(0);
}
//等待管道前面的命令完成
if(waitpid(pid2,&status2,0) == -1){
printf("wait for child process error\n");
}
//执行管道后面的命令
if(!(find_command(argnext[0])){
printf("%s :command not found !\n",argnext[0]);
exit(0);
}
if((fd2 = open("/tmp/tempfile",O_RDONLY)) == -1) {
perror("open");
}
//用文件代替键盘,执行第二条命令
dup2(fd2,0);
execvp(argnext[0],argnext);
if(remove("/tmp/tempfile") == -1) {//移除文件
perror("remove");
}
exit(0);
}
break;
default :
break;
}
//杀死父进程,后台进行
if(backgroud == 1 && pid != 0){
printf("[process id %d]\n",pid);
return;
}
//父进程等待子进程
if(waitpid(pid,&status,0) == -1){
printf("wait for child process error!\n");
}
}
static int find_command(char *command) {
DIR *dp;
struct dirent *dirp;
char *path[] = {"./","/bin","/usr/bin",NULL};
//使当前目录下可执行文件能够执行
if(strncmp(command,"./",2) == 0) {
command = command + 2;
}
int i = 0;
//在当前目录,/bin和/usr/bin中执行
while(path[i] != NULL) {
if( (dp = opendir(path[i])) == NULL) {//打开目录
printf("can not open /bin \n");
}
while( (dirp = readdir(dp)) != NULL) { //读取目录中的文件
if(strcmp(dirp->d_name,command) == 0) {//找到
closedir(dp);
return 1; //返回1
}
}
closedir(dp);
i++; //去找下一个目录
}
return 0; //没找到
}
#endif
makefile
cc = gcc
OBJ = myshell
all :
$(cc) -c ./*.c
$(cc) -o $(OBJ) ./*.o -I /usr/lib/x86_64-linux-gnu/libreadline.so -lreadline -ltermcap -g
rm -rf *.o
clean:
rm -rf *.o