LinuxC编程——ls的实现(-R,-a,-l)

  • Post author:
  • Post category:linux


Linux下的系统编程指的是程序员使用系统调用或C语言本身所携带的库函数来设计和编写某一特定的程序

ls是日常所用的比较常见的命令之一,那么如何实现ls命令

首先实现ls要明确参数的作用是什么

ls -a 可以将目录中的所有文件(包括以.开头的文件)显示出来
ls -l 列出文件中的所有信息,包括文件的属性和权限等数据
ls -R 使用递归连同目录中的子目录中的文件显示出来,如果要显示隐藏文件就要添加-a参数

在实现ls之前我们需要了解如下的信息

用户权限所对应的表

字符长量值 字符常量对应的八进制值 含义
S_IRUSR(S_IREAD) 00400 文件所有者具有可读权限
S_IWUSR(S_IWRITE) 00200 文件所有者具有可写入权限
S_IXUSR(S_IEXEC) 00100 文件所有者具有可执行权限
S_IRGRP 00040 用户组具有可读取权限
S_IWGRP 00020 用户组具有可写入权限
S_IXGRP 00010 用户组具有可执行权限
S_IROTH 00004 其他用户具有可读权限
S_IWOTH 00002 其他用户具有可写入权限
S_ISUID 04000 文件的 (set user-id)位
S_ISGID 02000 文件的(set group-id)位
S_ISVTX 01000 文件的 sticky位

在ls的输出过程中,需要粗略的计算终端的宽度,在这里我介绍的是isatty和ioctl函数

isatty函数是用来判断目前所输出的设备是不是为终端设备
ioctl可以获得终端的设备信息,一些特殊的设备文件都可以利用这个来获得

具体用法如下:(在这个用法中,我将 terminalWidth 作为全局变量

void getWidth(){
    struct winsize wbuf;
    terminalWidth = 80;
    if( isatty(STDOUT_FILENO) ){
       if(ioctl(STDOUT_FILENO, TIOCGWINSZ, &wbuf) == -1 || wbuf.ws_col == 0){
        return ;  
        }
        else
            terminalWidth = wbuf.ws_col;
    }

}

我们还应该了解如何获得文件的属性

#include < sys/types.h>

#include < sys/stat.h>

#include < unistd.h>

int stat(const char *pathname, struct stat *statbuf);

int fstat(int fd, struct stat *statbuf);

int lstat(const char

pathname, struct stat *statbuf);*

很多小伙伴们肯定对应该使用哪个函数感到迷惑,这三个函数的区别为, stat 用于获取由参数file name 所指定的文件名信息,保存在struct stat * buf 中,fstat 与 stat 的区别 fstat 是通过文件描述符来制定文件的, lstat 与 stat 的区别在于,对于 符号链接文件 , lstat 返回的是 符号链接本身, 而stat 指向的是文件状态信息

2。所以我在这里推荐大家是用的是,lstat 这个函数, 而对于如何读取链接文件,在这里我们可以使用 readlink 这个函数 来获取 文件中的链接

我在这里还遇到了一些错误,首先来跟大家说说readlink 这个函数应该如何应用

这个函数既是shell中的命令,又是操作系统提供的接口

通过查阅man 手册我们可以看到

#include < unistd.h>

ssize_t readlink(const char *pathname, char *buf, size_t bufsiz);

#include < fcntl.h> /* Definition of AT_* constants */

#include < unistd.h>

ssize_t readlinkat(int dirfd, const char *pathname,

char *buf, size_t bufsiz);

首先它的返回值是ssize_t类型 ,在32位的机器上 它与 int 类型是一致的,而在 64 位的机器上 它的类型与 long int 类型是一致的

它可以读取目录中的内容但是它不以文件控制符号来结尾’\0’,也就是说必须得添加’\0’才能使用

我在实现readlink的过程中遇到了如下的错误:

1.没有添加’\0’导致每次输出自己所读取的链接就会遇到段错误,即访问了不该访问的内存

2.我采用的是动态数组,动态数组是在堆中申请的,而内核为了方便效率,在申请的动态数组中是有内容的,所以在使用的过程中动态数组必须进行初始化,不然就会读取其他很多不相干的内容

在 struct stat * buf 中是一个保存文件信息的结构体

struct stat {

dev_t st_dev;//文件的设备编号

ino_t st_ino;//文件的i-node编号

mode_t st_mode_t//文件的类型和存取的权限

nlink_t st_nink;//文件的硬链接数目

uid_t st_uid;//文件所有者的id

gid_t st_gid//文件所有组的id

dev_t st_rdev;//若文件为设备文件,则为其设备编号

off_t st_size//文件的大小

blksize_t st_blksize//文件系统的缓冲区

blkcnt_t st_blocks//占有文件的区块的个数

time_t st_atime//文件最近一次被访问的时间

time_t st_mtime//文件最后一次被修改的时间

time_t st_ctime//文件最近一次被更改的时间

st_mode所包含的文件信息

S_ISLNK 符号链接

S_ISREG 一般文件

S__ISDIR 目录文件

S_ISCHR 为字符设备文件

S_ISBLK 块设备文件

S_ISFIFO 判断是不是为FIFO

S_ISSOCK 判断是不是为 套接字

了解如何获取目录信息,只要对目录是由读的权限,就可以获取目录信息

1.opendir

DIR

opendir(const char

name)

用来获取参数指定的目录,并且返回DIR*的目录流,类似与文件操作中的文件描述符,接下来对目录的读取和搜索都要使用此返回值,成功则返回DIR * 的目录流

2.readdir

用来从DIR中读取中目录项信息,类似于链表的遍历输入完成后输出NULLd_name

3.closedir

关闭dir所指向的目录

用法如下

#include< sys/types.h>

#include< dirent.h>

#include < stdio.h>

int readir(const char * path)

{

DIR * dir;

struct dirent * prt;

if((dir = opendir(path)==NULL)

perror(“opendir”);

while((ptr=readdir(dir)!=NULL)

{

printf(“%d” ptr->d_name);

}

closedir(dir);

return 0;

}

在实现递归的过程中,可以采用

rewinddir 函数 十分重要


在这里我将解释以下 rewinndir的函数作用,以及为什么它在递归的过程中相当重要

头文件:

#include < sys/types.h>

#include < dirent.h>

定义的函数:

void rewinddir(DIR *dir);

官方给出的说明是rewinddir()用来设置参数dir 目录流目前的读取位置为原来开头的读取位置.

可以这么理解,打印出上级目录的时候已经进入到当前目录的最后一个,使用rewinddir就可以返回第一个文件中

我在实现 ls -R 遇到的问题即自己的解决办法

1.遭遇段错误

可能原因:栈溢出

-R 递归会产生巨大的文件量,使用普通的静态数组是无法满足这种需求,即使范围在大也不可以,在这里我们应该使用 动态数组或者 链表 来实现这个功能

2.在遍历/proc/目录的过程中偶尔会遭遇错误,大部分情况可以实现

可能原因:文件读取失败

解决方式; lstat 来获取文件信息如果获取失败就返回上层

/proc/是虚拟文件系统,它在里面保存进程,并且会定期清除,可能在读取的同时,和删除这个文件,即读取失败

在实现ls -R 的时候一定要明确自己思路

我的思路是,先输出第一层目录,然后通过rewinddir 返回到文件流,然后判断它是不是目录,如果是目录则进入,当一层没有目录的时候,就return 返回上层

代码奉上

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>
#include <dirent.h>
#include<stdlib.h>
#include<sys/ioctl.h>
int terminalWidth ;
char filenames[256][PATH_MAX];
char filenames1[256][100];
int alfag=0,lflag=0;//参数种类,对-l和-a,-r,-R进行判断
int cnt=0;
void Quicksort(int cnt1,char filenames2[][PATH_MAX],int start);
void recursion(char * path,int alfag);
int ls_prepare(char *path,int lflag);
int ls_prepare(char *pathname,int lflag);
int _ls(char *path,int lflag,int alfag);
int colormode,foreground,background;    
void getcolor( char *filename ){
    struct stat info;    //通过设置可以获取文件属性
    foreground=0;
   lstat( filename,&info );//将文件输入获得赋值给info
//    foreground = 37;
    switch ( (info.st_mode & S_IFMT) ){
        case S_IFREG:               /*regular 普通文件 , 色*/
            {
                foreground = 37;
                    if((info.st_mode&S_IXOTH)||(info.st_mode&S_IXGRP)||(info.st_mode&S_IXUSR))
      {
            foreground = 32;
        }
            break;
            }
        case S_IFLNK:               /*symbolic link 链接文件 , 青蓝色*/
            foreground = 36;
            break;
        case S_IFSOCK:              /*紫红色*/
            foreground = 35;
            break;
        case S_IFDIR:               /*directory 目录文件 , 蓝色*/
        foreground = 34;
            break;
        case S_IFBLK:               /*block special 块设备文件 , 黄色*/
            foreground = 33;
            break;
        case S_IFCHR:               /*character special 字符设备文件 , 黄色*/
            foreground = 33;
            break;
    }
}
struct filename
{
    char filename[PATH_MAX];
    struct filename * next;
};
void Quicksort1(struct filename * pHead,struct filename * tail)
{
    if(pHead->next==tail)
        return ;
    if(pHead->next->next==tail)
    {
            return ;
    }
    struct filename * mid = pHead->next;
    struct filename * p = pHead;
    struct filename * q =mid;
    struct filename * t = mid->next;
    char name[MAXNAMLEN];
    strcpy(name,mid->filename);
    while(t!=tail)
    {
        if(strcmp(t->filename,name)<0)
            p=p->next=t;
        else
            q=q->next=t;
        t=t->next;
    }
    p->next= mid;
    q->next =tail;
    Quicksort1( pHead ,mid );
    Quicksort1( mid  , tail);
}
void list_R(const char * dirname,int lflag)
{
        DIR     *dir_ptr;       
        struct dirent   *direntp;       
        char        *fullpath;
        struct stat info;
//        int i;
        struct filename * pHead,*p1;
        pHead=p1=(struct filename *)malloc(sizeof(struct filename));
        pHead->next=p1->next=NULL;
        printf("%s:\n",dirname);
        if ( ( dir_ptr = opendir( dirname ) ) == NULL ){
            fprintf(stderr,"list_R: cannot open %s\n", dirname);
            return;
        }
     //  printf("%s:\n",dirname);
//        i=0;
        fullpath = (char *)malloc(strlen(dirname) + 1 + MAXNAMLEN + 1);
        while ( ( direntp = readdir( dir_ptr ) ) != NULL )
        {   
            struct filename *new1=(struct filename *)malloc(sizeof(struct filename));
            if(alfag==0)
           { 
            if(direntp->d_name[0]=='.')
            {
                continue;
            }
          }          

             sprintf(fullpath,"%s/%s",dirname,direntp->d_name);
         if(lflag==3)
             _ls(fullpath,lflag,alfag);
             else
             {
                   strcpy(new1->filename,direntp->d_name);
                   p1->next=new1;
                   p1=new1;
                   new1->next=NULL;
            }
        }
        Quicksort1(pHead,NULL);
       struct filename *  pTemp=pHead->next;
        struct filename * q=pHead;
        if(pHead->next!=NULL)
        {
            while(pTemp!=NULL)
        {
            getcolor(pTemp->filename);
            printf("\033[%dm%s\033[0m\n",foreground,pTemp->filename);
            free(q); 
            q=pTemp;
            pTemp=pTemp->next;
        }
        }
        if ( lflag ){
            rewinddir(dir_ptr);
            while ( ( direntp = readdir( dir_ptr ) ) != NULL )
            {
             if(alfag==0)
          { 
              if(direntp->d_name[0]=='.')
             {
               continue;
             }
           }
                sprintf(fullpath,"%s/%s",dirname,direntp->d_name);
            //                printf("%s",direntp->d_name);
           //   puts("");
               lstat(fullpath,&info);
        //       _ls(fullpath,lflag,alfag);
        if(alfag==0)
        {
            if(S_ISDIR(info.st_mode))
                  {
                        putchar('\n');
                        list_R( fullpath, lflag );
                    }
        }
        else
        {
            if ( S_ISDIR(info.st_mode)&& direntp->d_name[0]!='.')
                    //
                     {
                    putchar('\n');
                    list_R( fullpath, lflag );
                }
             }
        }
        }
        closedir(dir_ptr);
        free(fullpath);
    }
void Quicksort(int cnt1,char filenames2[][PATH_MAX],int start)
{
    int i,j;
    char *name;
    char *name1;
    name=(char *)malloc(MAXNAMLEN);
    name1=(char *)malloc(MAXNAMLEN);
    memset(name,0,MAXNAMLEN);
    memset(name,0,MAXNAMLEN);
    if(start >cnt1)
        return ;
    strcpy(name,filenames2[start]);
    i=start;
    j=cnt1;
    while(i!=j)
    {
        while(strcmp(filenames2[j],name)<=0 && i<j)
            j--;
        while(strcmp(filenames2[i],name)>=0 && i<j)
            i++;
        if(i<j)
        {
                strcpy(name1,filenames2[i]);
           strcpy(filenames2[i],filenames2[j]);
            strcpy(filenames2[j],name1);
      }
    }
    strcpy(filenames2[start],filenames2[i]);
    strcpy(filenames2[i],name);
       Quicksort(i-1,filenames2,start);
       Quicksort(cnt1,filenames2,i+1);
       return ;
       free(name);
       free(name1);
}
void getWidth(){
    struct winsize wbuf;
    terminalWidth = 80;
    if( isatty(STDOUT_FILENO) ){
        if(ioctl(STDOUT_FILENO, TIOCGWINSZ, &wbuf) == -1 || wbuf.ws_col == 0){
        }
        else
            terminalWidth = wbuf.ws_col;
    }
    return ;
}
int getlen_str(char * str)
{
    int res=0,i;
    while(*str!='\0')
    {
        if(*str > 0)
        {
            res++;
            str++;
        }
        else
        {
            for(i=7;i>=0;i--)
            {
                if(!((*str>>7)&1))
                    break;
                res+=2;
                str+=7-i;
             //   if((*str>>7)&1)
            //        break;
            }
        }
    }
    return res;
}
void display_single(int cnt,int alfag)
{
    int i,k,n=0,m=0,j=0,n1;
   n=cnt,n1=cnt;
   i=0;
  Quicksort(cnt,filenames,0);
    while(n>=0)
    {
        n--;
        strcpy(filenames1[i],filenames[i]);
        for(m=0;m<((int)strlen(filenames[i]));m++)
        {

            if((filenames[i][m]=='.'&&filenames[i][m-1]=='/'))
             {   
                 break ;
             }
             if(filenames[i][m]=='/')
             {
                 k=m;
           }
        }   
              for(j=0;j<=k;j++)
           {
               filenames[i][j]=' ';
            }
                                  if(alfag==0)
                     {

                       if(filenames[i][k+1]=='.')
                       {
                                strcpy(filenames[i]," ");
                        }
                     }
        i++;
    }
    Quicksort(cnt,filenames,0);
    int wordColNUM=20;
     cnt=cnt<wordColNUM?cnt:wordColNUM;//列
    while(cnt>0)
    {  int wordLenMax[4000]={0};//窗口的长度
      int wordRowNum=0;//窗口的行
      j=0;
      int sum=0;
      int length[4000];
      for(j=0;j<n1;j++)
      {
          length[j]=getlen_str(filenames[j]);
      }
     wordRowNum=(n1%cnt)?(n1/cnt):(n1/cnt+1);//行
     if((cnt-1)*wordRowNum>=terminalWidth)
         cnt-- ;
     for(j=0;j<cnt;j++)
     {
         int f=j*wordRowNum;
         for(i=0;i<wordRowNum;i++)//求出行数最宽的是
      {
          if(f>n1)
              break;
          if(getlen_str(filenames[f])>wordLenMax[j])
              wordLenMax[j]=(getlen_str(filenames[f]));
          f++;
      }
         if(j!=cnt-1)
         sum+=wordLenMax[j]+2;
     }
     if(sum<terminalWidth)
     {
       //  cnt--;
         int t=wordRowNum - cnt * wordRowNum +n1;
         for(int p=0;p<wordRowNum;p++)
         {
             for(int o=0;o<cnt-1;o++)
             {
               getcolor(filenames1[p+o*wordRowNum]);
                 printf("\033[%dm%s\033[0m",foreground,filenames[p+o*wordRowNum]);
                 for(int y=0;y<wordLenMax[o]-length[p+o*wordRowNum];y++)
                 printf(" ");
                 for( int g=0;g<2;g++)
                    printf(" ");
          //   printf("\n");
            }
             if(t)
             {
                 getcolor(filenames1[p+(cnt-1 )*wordRowNum]);
                 printf("\033[%dm%s\033[0m",foreground,filenames[p+(cnt-1)*wordRowNum]);
                 t--;
             }
             printf("\n");
         }            
         break;
     }
 //     wordLenMax +=2; //空格防止一组由多行输出;
 //     wordRowNum = terminalWidth/wordLenMax;    //面积除宽等于列的长度
     cnt--;
     if(!cnt)
     {
         cnt=1;
         for(int x=0;x<n;x++)
         {
             getcolor(filenames1[x]);
             printf("\033[%dm%s\033[0m\n",foreground,filenames[x]);

         }
     }
    }
      return ;
}
int _ls(char *path,int lflag,int alfag)
{
    int i,n,k;
    int  temp;
    struct stat buf;
    char *out=NULL;
//   char out[PATH_MAX];
    struct passwd *pw=NULL;
    struct group *gr=NULL;
    char *t=NULL;
    char *name=NULL;
    char *name1=NULL;
      name = (char *)malloc(strlen(path) + 1 + MAXNAMLEN + 1);
      name1 =(char *)malloc(strlen(path)+1 +MAXNAMLEN +1);
      out=(char *)malloc(PATH_MAX);
        memset(out,0,PATH_MAX);
   //   j=strlen(out);
    strcpy(name,path);
    strcpy(name1,path);
    for(i=0;i<((int)strlen(name));i++)
    {
         if((name[i]=='.'&&name[i-1]=='/')&&alfag==0)
         {   // cnt--;
             return 0;
         }
         if(name[i]=='/')
            k=i;
    }
    for(i=0;i<=k;i++)
    {
         name[i]=' ';     
    }
    if(lflag==2)
        return 0;
   // lstat(path,&buf);
    if(lstat(path,&buf)<0)
    {
    //  fprintf(stderr,"stat error:%s\n",strerror(errno));
        return 0 ;
    }
    //获取字符串的属性:普通文件-、目录d、字符设备c、块设备b、
    //管道文件p、连接文件l、套接字文件s
    //st.mode表示的是文件的权限和文件的用户
        switch(buf.st_mode & S_IFMT)        //S_IFMT表示位遮罩
    {
    case S_IFREG:   
        {
            printf("-");
        }
        break;
    case S_IFDIR:   
        {
            printf("d");
        }
        break;
    case S_IFCHR:   
        {
            printf("c");
        }
        break;
    case S_IFBLK:   
        {
            printf("b");
        }
        break;
    case S_IFIFO:   
        {
            printf("p");
        }
        break;
    case S_IFLNK:   
        {
            printf("l");
        }
        break;
    case S_IFSOCK:  
        {
            printf("s");
        }
        break;
    }
    //打印文件的读写属性:读r、写w、执行x、无权限-
    for(n=8;n>=0;n--)       
    {
        if(buf.st_mode&(1<<n))
        {
            switch(n%3)
            {
            case 2:
                printf("r");
                break;
            case 1:
                printf("w");
                break;
            case 0:
                {
                    printf("x");
                }
                    break;
            default:
                break;
            }
        }
        else
        {
            printf("-");
        }
    }

    //硬链接数,此链接非彼链接,指(包含)目录的个数,
    //文件为1,目录起始为2,再加上目录里包含的目录个数(不递归,只一层)
    printf(" %4d ",(int)buf.st_nlink);      
    pw = getpwuid(buf.st_uid);      //所属用户名//可以得到用户指定的信息
                 //根据struct passswd * getpwuid来进行判断
        printf(" %s ",pw->pw_name);     


    gr = getgrgid(buf.st_gid);      //所属组名
    printf(" %6s ",gr->gr_name);
    printf(" %8ld ",buf.st_size);       //字节计总大小
    t = ctime(&buf.st_atime);   //最后一次访问时间
     printf(" %10.12s ",4+t);
     getcolor(path);
               printf("\033[%dm%4s\033[0m",foreground,name);
    //判断是否为链接,是返回真   
    if(S_ISLNK(buf.st_mode))        
    {
             printf(" -> "); 
        if((temp=readlink(name1,out,PATH_MAX)==-1))//读取链接数失败
        {
        //  printf("readlink error");
        }
        strcat(out,"\0");
        printf("%4s",out);
    }
    printf("\n");
    free(name);
    free(name1);
    free(out);
    return 0;
}
// ls的准备工作
int ls_prepare(char *path,int lflag)        
{
    struct stat buf;        //man lstat可以看到此结构
    DIR *dir;       //opendir会返回一个类似于dir的数据流
    struct dirent *ptr;
    int count = 0;
    //man readdir可以看到此结构
    //获取文件/目录属性并赋值给buf,该函数和lstat一样,
    //只是当w为链接时,指代他本身,并不存在文件
lstat(path,&buf);
    /*  if(lstat(path,&buf)<0)      
    {
        fprintf(stderr,"stat error:%s\n",strerror(errno));
        return -1;
    }*/
    dir=opendir(path);
    while((ptr= readdir(dir))!=NULL)
    {
            count++;
    }
    closedir(dir);
     int i,len=strlen(path);
     dir=opendir(path);
     for(i=0;i<count;i++)
     {
         ptr=readdir(dir);
         if(ptr==NULL)
         {
             perror("readdir");
         }
         strcpy(filenames[i],path);
         filenames[i][len]='\0';
         strcat(filenames[i],ptr->d_name);
         filenames[i][len+strlen(ptr->d_name)]='\0';
     }
     cnt=i;
    //S_IRUSR表示读权限,S_IWUSR表示写权限 
  Quicksort(cnt-1,filenames,0);
    if(lflag==0)
    {
        display_single(cnt,alfag);
        return 0;
    }
        for(i=0;i<count;i++)
        {
            _ls(filenames[i],lflag,alfag);
    }
   closedir(dir);   
    return 0;
}
int main(int argc,char ** argv)
{
   getWidth();
    char param[32];//用来保存命令行的参数,命令行的形式一般为 -l -al -a
   char path[81];
   struct stat buf;
   int i,j,k,num=0;
    //命令行中实现解析
    j=0;
   for(i=1;i<argc;i++)
   {
       if(argv[i][0]=='-')
       {
           for(k=1;k<(int)strlen(argv[i]);k++,j++)
           {
                param[j] = argv[i][k];
           }
           num++;
       }
   }
  for(i=0;i<j;i++)
  {
        if(param[i]=='a')
        {
            alfag=1;
            continue;
        }
        if(param[i]=='l')
        {
          lflag+=1;
          continue;
        }
        if(param[i]=='R')
        {
            lflag=lflag+2;
            continue;
        }
  }
  param[j]='\0';
  //如果在其后没有输入文件名或者目录,默认为当前目录
  if((num+1)==argc)
  {
      strcpy(path,"./");
      path[2]='\0';
      ls_prepare(path,lflag);
      return 0;     
  }
  i=1;
  do
  {
      if(argv[i][0]=='-')
      {
          i++;
          continue;
      }
      else
      {
          strcpy(path,argv[i]);
          if(stat(path,&buf)==-1)
          {
              perror("stat");
          }
          if(S_ISDIR(buf.st_mode))//argv[i]是一个目录
           //   ,如果目录的最后一个字符不是'/',添加
          {
                    if(path[strlen(argv[i])-1]!='/')
                    {
                        path[strlen(argv[i])]='/';
                        path[strlen(argv[i])+1]='\0';//添加文件结束符号
                    }
                    else  
                        path[strlen(argv[i])]='\0';
                    if(lflag>=2)
                    {
                      list_R(path,lflag);
                       return 0;
                     }  
                        ls_prepare(path,lflag);
                        i++;
          }
          else{
                        display_single(cnt,alfag);
                        i++;
          }
      }
  }while(i<argc);
return 0;
}



版权声明:本文为dream0130__原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。