C语言实现shell (包括管道输入输出重定向)
Posted 玛丽莲茼蒿
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C语言实现shell (包括管道输入输出重定向)相关的知识,希望对你有一定的参考价值。
一、实现的命令及功能
- ls命令(实际上实现的是ls -l命令)
- echo
- cat
- mkdir
- rm
- cd
- pwd
- cp
- wc
- rmdir
- 输入输出重定向
- 管道
二、分模块讲解
1.用到的所有头文件
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <pwd.h>
#include <grp.h>
#include<wait.h>
#include <time.h> //解析文件的时间属性
#include <dirent.h> //打开目录,读目录,关目录
2.ls命令(实际上实现的是ls -l命令)
(1)实现功能
打印输出当前目录下的所有目录和文件的属性
(2)步骤
1)需要找到该路径下所有目录和文件的名字
2)找到当前路径,需要头文件#include<unistd.h>,调用函数getcwd():
char basePath[100];
getcwd(basePath,sizeof(basePath));
将得到的当前路径放在basePath里
3)在当前路径下找到所有文件、目录的名字
用到的头文件:#include <dirent.h>
用到的结构体:
DIR *dir; //目录指针指向当前目录
struct dirent *ptr; //循环指向每一个文件。
用opendir(绝对路径)函数打开当前目录,用readdir(目录指针dir)函数,循环读取每一个文件和目录的名字;最后用closedir(目录指针dir)函数关闭目录
4)调用stat函数,使用上一步得到的文件(目录)名作为参数,可以得到一系列的文件属性。
用到的头文件:#include <sys/types.h>、 #include <sys/stat.h> 、#include <unistd.h>
用到的结构体
struct stat info; //文件(目录)的属性信息
用到的函数:
stat(filename,&info); //将文件filename的以下各种属性存放在info中
5)stat结构体种只包含了部分ls命令列出来的文件属性,如st_mode、st_size等,可以直接打印出来。例如,st_mode不需要解析。
6)文件的其他的属性需要调用更底层的函数进行进一步的解析。
• getpwuid函数:解码所有者信息(解析struct stat中的st_uid数据)
struct passwd *pswd;
pswd=getpwuid(info.st_uid);
• getgrgid函数:解码所属组信息(解析struct stat中的 st_gid数据)
struct group *grp;
grp=getgrgid(info.st_gid);
• localtime函数:解码上次访问时间信息 (解析struct stat中的 st_atime数据)
struct tm *atime;
atime = localtime(&info.st_mtime);
(3)完整代码
/*
实现ls命令
用法:ls
*/
void ls(){
DIR *dir;
struct dirent *ptr;
//得到绝对路径
char basePath[100];
getcwd(basePath,sizeof(basePath)); //get当前路径 ,放在basePath里
if ((dir=opendir(basePath)) == NULL)
{
perror("Open dir error...");
exit(1);
}
while ((ptr=readdir(dir)) != NULL)
{
if(strcmp(ptr->d_name,".")==0 || strcmp(ptr->d_name,"..")==0) ///current dir OR parrent dir
continue;
else if(ptr->d_type == 8) ///file
//printf("%s\\n",ptr->d_name);
lsCore(ptr->d_name);
else if(ptr->d_type == 10) ///link file
//printf("%s\\n",ptr->d_name);
lsCore(ptr->d_name);
else if(ptr->d_type == 4) ///dir
{
//printf("%s\\n",ptr->d_name);
lsCore(ptr->d_name);
}
}
closedir(dir);
}
/*
本函数实现ls命令的核心功能;从ls函数中得到文件名,打印文件属性
*/
void lsCore(char *filename){
struct stat info;
stat(filename,&info);
/*-----打印类型,权限----*/
switch(info.st_mode & S_IFMT)
{
case S_IFREG:printf("-");break;
case S_IFDIR:printf("d");break;
case S_IFLNK:printf("l");break;
case S_IFCHR:printf("c");break;
case S_IFBLK:printf("b");break;
case S_IFIFO:printf("p");break;
case S_IFSOCK:printf("s");break;
}
char rwx[]={'r','w','x'};
for(int i=0; i<10; i++)
{
printf("%c",info.st_mode & (0400>>i) ? rwx[i%3] : '-');
}
/*-----打印所有者 ----*/
struct passwd *pswd;
pswd=getpwuid(info.st_uid);
printf(" %s",pswd->pw_name);
/*-----所属组----*/
struct group *grp;
grp=getgrgid(info.st_gid);
printf(" %s",grp->gr_name);
/*-----打印文件大小----*/
printf(" %ld ",info.st_size);
/*-----打印最近时间 ----*/
struct tm *atime;
atime = localtime(&info.st_mtime);
printf("%d-%d-%d %d:%d "
,atime->tm_year+1900
,atime->tm_mon+1,atime->tm_mday
,atime->tm_hour,atime->tm_min);
/*-----打印文件名----*/
printf(" %s\\n",filename);
}
3.echo
/*
实现echo函数
实现功能:回显echo命令的参数
用法:echo hello dad!
*/
void echo(char *argv[]){
int i=1;
while(argv[i]!=NULL){
printf("%s ",argv[i]);
i++;
}
printf("\\n");
}
4.cat
/*
实现cat函数
实现功能:对文件1.txt内容进行标准输出
用法:cat 1.txt 或 cat /home/zxf/1.txt
*/
void cat(char *argv[]){
char buf[200]; //模拟缓冲区
int fp;
fp=open(argv[1],O_RDONLY);
if(fp==-1)
{
printf("open file failed\\n");
return;
}
int n;
while((n=read(fp,buf,199))>0)
write(1,buf,n); //1 标准
close(fp);
}
5.mkdir
/*
实现mkdir函数
实现功能:根据绝对路径或相对路径创建新的目录
用法 :mkdir newdir或 mkdir /home/zxf/newdir
*/
void createDir(char *argv[]){
/*---------------实现方法一----------------*/
if(mkdir(argv[1],0775)!=0){ //return 0 if on success
printf("create dir failed\\n");
}
/*---------------实现方法二----------------*/
// if(mkdirat(AT_FDCWD,argv[1],0775)!=0){ //argv[1]是绝对路径时,目录号被忽略
// printf("create dir failed\\n");
// }
}
6.rm
/*
实现rm函数
实现功能:根据绝对路径或者相对路径删除文件(必须是空文件)
用法:rm 1.txt 或 rmdir /home/zxf/1.txt
*/
void deleteFile(char *argv[]){
//if(remove(argv[1])!=0){ //连文件夹都能删,感觉remove函数比较危险,换成unlink函数
if(unlink(argv[1])!=0){
printf("delete empty file failed\\n");
}
}
7.cd
/*
实现cd函数
实现功能:进入相对路径或绝对路径,或回退到上一目录
用法:cd dir 或 cd /home/zxf/dir或 cd ..
*/
void cd(int argc, char *argv[]){
char buf[100];
if (chdir(argv[1])>=0)
{
getcwd(buf,sizeof(buf)); //get绝对路径
printf("Current absolute path is:%s\\n\\n",buf);
}
}
8.rmdir
/*
实现rmdir函数
实现功能:根据绝对路径或者相对路径删除目录(必须是空目录)
用法:rmdir dir 或 rmdir /home/zxf/dir
*/
void deleteDir(char *argv[]){
if(rmdir(argv[1])!=0){
printf("delete empty dir failed\\n");
}
}
9.wc
(1)实现功能
统计指定文件的行数、字符个数、单词个数
(2)实现步骤
1)统计文件行数
首先用fopen函数以只读方式打开文件,返回文件句柄,然后用fgets函数按行获取文件,每获取一行就让统计行数的变量加一。直到文件末尾,返回NULL,行数统计完毕。
关键代码:
while(fgets(s,200,f)!=NULL)
{
count_line++;
}
2)统计单词个数
设计双层循环,最外层循环读取每一行,遇到一个空格符或换行符则单词数加一。也就是说以下的情况都作为一个单词:“abc”、“!!”、“{}”“__abc”。
关键代码:
int temp;
while((temp=fgetc(f))!=EOF)
{
char temp2=temp; //int 向 char转换
if(temp2==' '||temp2=='\\n')
count_word++;
}
3)统计字符个数
统计字符个数用到了fgetc函数,以字符为单位读取文件。没读取一个字符,统计字符个数的变量加一。直到文件末尾。
关键代码:
while(fgetc(f)!=EOF)
{
count_char++;
}
4)最后一定要关闭文件
(3)完整代码
/*
实现wc函数
实现功能:统计制定文件的行数、单词个数、字符个数
用法: wc shell.c
*/
void wc(char *argv[]){
FILE *f,*f1,*f2,*f3;
int count_line=0;
int count_word=0;
int count_char=0;
char s[201];
/*-------统计行数------*/
if((f=fopen(argv[1],"r"))==NULL)
printf("open file failed\\n");
while(fgets(s,200,f)!=NULL)
{
count_line++;
}
printf("%d ",count_line);
/*-----统计单词数-------*/
if((f=fopen(argv[1],"r"))==NULL)
printf("open file failed\\n");
int temp;
while((temp=fgetc(f))!=EOF)
{
char temp2=temp; //int 向 char转换
if(temp2==' '||temp2=='\\n')
count_word++;
}
printf("%d ",count_word);
/*------统计字符个数-----*/
if((f=fopen(argv[1],"r"))==NULL)
printf("open file failed\\n");
while(fgetc(f)!=EOF)
{
count_char++;
}
printf("%d ",count_char);
/*------打印文件名------*/
printf("%s\\n",argv[1]);
fclose(f);
}
10.cp
/*
实现cp函数
实现功能:将一个文件拷贝到另一个文件
用法:cp 1.txt 2.txt
*/
void cp(char* argv[]) //自定义的一个外部命令:将argv[1]argv[2]的内容拷贝到argv[3]里
{
int fd1,fd2,n;
char buf[512];
fd1=open(argv[1],O_RDONLY);
fd2=creat(argv[2],0644);
while((n=read(fd1,buf, 512))>0)
write(fd2,buf,n);
close(fd1);
close(fd2);
}
11. pwd
/*
实现pwd函数
实现功能:显示当前路径
用法:pwd
*/
void pwd(){
char buf[100];
getcwd(buf,sizeof(buf)); //get当前路径
printf("Current absolute path is:%s\\n\\n",buf);
}
12.主函数部分(包括了输入输出重定向、管道)
int argc = 0; //存放指令的参数个数
char* argv[32] = {NULL}; //指令
/*
实现任意长度命令的分割
举例:输入命令cat 1.txt ,则*argv[0] 存放cat; *argv[1]存放1.txt, *argv[2]存放NULL ,argc=3
返回0代表命令有效,返回-1代表命令无效
*/
int div_command(char* buf)
{
if (buf == NULL)
return -1;
argc = 0;
char* front = buf;
char* back = buf;
//不断分割命令成指针数组
while (1)
{
while (*front == ' ')//避免用户在输入命令前输入了多个空格
front++;
if (*front == '\\0')
break;
back = front;
while (*back != ' ' && *back != '\\0')//这两句,找到小字符串尾
back++;
if (*back == '\\0')
{
argv[argc++] = front;
break;
}
*back = '\\0'; //一个字符串分割成多个字符串"mv hhhh aaaa\\0"->"mv\\0 hhhh\\0 aaaa\\0 null"
argv[argc++] = front;//argv[i]里存小字符串的首地址
front = back + 1;
}
argv[argc++] = NULL; //argv[]的最后一位必须是NULL
if (argc == 1)//输入的命令什么有用信息也没有
return -1;
return 0;
}
/*
命令列表
*/
void list()
{
printf("----------------------------------------------------\\n");
printf("cd : change path\\n");
printf("pwd : print your current path\\n");
printf("ls : list directory contents\\n");
printf("echo : display a line of textn");
printf("cat : print file on the standard output\\n");
printf("mkdir: create a new directory\\n");
printf("rmdir: delete a directory\\n");
printf("rm : delete a file\\n");
printf("wc : print newline, word, and byte counts of a file \\n");
printf("cp : copy files\\n");
printf("> : output redirection\\n");
printf("< : input redirection\\n");
printf("| : pipeline\\n");
printf("----------------------------------------------------\\n");
}
int main()
{
int i;
char command[1024];//接收到的命令
char *prompt="$ ";
int isBuiltin=0; //==1,是内置命令
int isCopy12=0; //==1,是自定义的copy12命令
list();
while(1)
//循环接受命令
{ //char absolutePath[30];
//getcwd(absolutePath,sizeof(absolutePath));
memset(command,0x00,1024);
printf("yourname@ubuntu: $");
//读取到\\n为止,删除缓冲区数据
scanf("%[^\\n]%*c", command);
以上是关于C语言实现shell (包括管道输入输出重定向)的主要内容,如果未能解决你的问题,请参考以下文章