操作系统第4次实验报告:文件系统

  • 姓名:程开
  • 学号:201821121060
  • 班级:计算1812

1. 编写程序

在服务器上用Vim编写一个程序:实现Linux系统命令ls -lai的功能,给出源代码。

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<time.h>
#include<unistd.h>
#include<sys/types.h>
#include<dirent.h>
#include<grp.h>
#include<pwd.h>
#include<errno.h>
#include<sys/stat.h>
#include<limits.h>
#include<assert.h>

void Rattribute(int argc,char *argv[],char *path);//读取文件属性 
void PriFileName(int mode,int uid,char *name);//打印文件名 
void PriExactInf(int mode,struct stat st);//打印文件详细信息 

int flag = 0;

int main(int argc,char *argv[])
{
    
    char path[]={"./"};//"./"表示当前工作目录,直接获取 
    Rattribute(argc,argv,path);
    DIR *dir = opendir(path);
    if(dir == NULL)
    {
        printf("No such file\n");
        exit(0);
    }
    struct dirent *fname = NULL;//有下面几种 
    /*struct dirent{
        long d_ino;  inode number 索引节点号 
        off_t d_off;  offset to this dirent 在目录文件中的偏移 
        unsigned short d_reclen;  length of this d_name 文件名长
        unsigned char d_type;  the type of d_name 文件类型 
        char d_name [NAME_MAX+1];  file name (null-terminated) 文件名,最长255字符 
        }
      */
    //调用readdir函数获取该目录中的目录项
    while((fname = readdir(dir)) != NULL)
    {    
        //当文件名第一个字符是.时,为隐藏文件,不输出
        if(((flag&1)==0) && (strncmp(fname->d_name,".",1) == 0))
        {
            continue;
        }
        struct stat st;
    /*struct stat {

        mode_t     st_mode;       //文件对应的模式,文件,目录等
        ino_t      st_ino;       //inode节点号
        dev_t      st_dev;        //设备号码
        dev_t      st_rdev;       //特殊设备号码
        nlink_t    st_nlink;      //文件的连接数
        uid_t      st_uid;        //文件所有者
        gid_t      st_gid;        //文件所有者对应的组
        off_t      st_size;       //普通文件,对应的文件字节数
        time_t     st_atime;      //文件最后被访问的时间
        time_t     st_mtime;      //文件内容最后被修改的时间
        time_t     st_ctime;      //文件状态改变时间
        blksize_t st_blksize;    //文件内容对应的块大小
        blkcnt_t   st_blocks;     //文件内容对应的块数量
      };*/
        char temp[128] = {0};
        strcpy(temp,path);
        strcat(temp,"/");
        strcat(temp,fname->d_name);
        stat(temp,&st);
        //-li
        if ((flag&2)==2)
        {
            //有参数i
            if((flag&4)==4)
            {
                printf("%ld  ",st.st_ino);
            }
            //有参数l
            PriExactInf(st.st_mode,st);
            PriFileName(st.st_mode,st.st_uid,fname->d_name);
            printf("\n");
            continue;
        }
        //-ai
        if((flag&4)==4)
        {
            printf("%ld  ",fname->d_ino);
            PriFileName(st.st_mode,st.st_uid,fname->d_name);
            continue;
        }
        //-a
        PriFileName(st.st_mode,st.st_uid,fname->d_name);
    }
    if(argc == 1||(argc >1&&flag == 0))
        printf("\n");
    closedir(dir);
}

void Rattribute(int argc,char *argv[],char *path)
{
    int i = 0;
    for(i = 1;i < argc; ++i)
    {    
        if(strncmp(argv[i],"-",1)==0)
        {
            if(strstr(argv[i],"a") != NULL)
            {
                flag |= 1 << 0; //-a参数显示该隐藏文件
            }
            if(strstr(argv[i],"l") != NULL)
            {
                flag |= 1 << 1;    //-l参数显示该文件的详细信息
            }
            if(strstr(argv[i],"i") != NULL)
            {
                flag |= 1 << 2;    //-i参数显示该文件的inode number
            }
            //位运算可使用一个变量同时标记多个参数是否传递
        }
        else
        {
            //直接路径,copy到path字符数组中
            if(strncmp(argv[i],"/",1) == 0)
            {
                strcpy(path,argv[i]);
            }
            //间接路径,将当前路径与所给路径连接
            else
            {
                strcat(path,"/");
                strcat(path,argv[i]);
            }
        }
    }
}
//用于输出文件名
void PriFileName(int mode,int uid,char *name)
{
    //是目录文件
    if(S_ISDIR(mode))
    {
        //文件名显示为蓝色
        printf("\33[1;34m%s\033[0m ",name);
    }
    //是普通文件
    else if(S_ISREG(mode))
    {
        if(mode & S_IXUSR||mode & S_IXGRP||mode & S_IXOTH)
        {
            if(uid==0) //属主用户,文件名显示为红色
                printf("\33[41;37m%s\033[0m ",name);
            else       //其他用户,文件名显示为绿色
                printf("\33[1;32m%s\033[0m ",name);
        }
        else
        {
            printf("%s ",name);
        }
    }
    else
    {
        printf("%s ",name);
    }
}
//用于输出文件详细信息
void PriExactInf(int mode,struct stat st)
{    //文件权限判断
    char str[10] = {"----------"};

    if(S_ISDIR(mode)) str[0] = 'd';
    if(S_ISCHR(mode)) str[0] = 'c';
    if(S_ISBLK(mode)) str[0] = 'b';

    if(mode & S_IRUSR) str[1] = 'r';
    if(mode & S_IWUSR) str[2] = 'w';
    if(mode & S_IXUSR) str[3] = 'x';

    if(mode & S_IRGRP) str[4] = 'r';
    if(mode & S_IWGRP) str[5] = 'w';
    if(mode & S_IXGRP) str[6] = 'x';

    if(mode & S_IROTH) str[7] = 'r';
    if(mode & S_IWOTH) str[8] = 'w';
    if(mode & S_IXOTH) str[9] = 'x';

    int i = 0;
    for(; i < 10; i++)
    {
        printf("%c",str[i]);
    }
    printf(". ");
    printf("%ld ",st.st_nlink);
    //输出属主
    struct passwd *pd = getpwuid(st.st_uid);
    assert(pd != NULL);
    printf("%4s ",pd->pw_name);
    //输出组用户
    struct group *gp = getgrgid(st.st_gid);
    assert(gp != NULL);
    printf("%4s ",gp->gr_name);
    //输出文件大小
    printf("%4ld ",st.st_size);
    //输出最近操作时间
    struct tm * lchangetime = localtime(&(st.st_mtime));
    printf("%d月%d号%d:%d ",(lchanon+1),lchangetime->tm_mday,lchangetime->tm_hour,lchangetime->tm_min);
}

2. 分析运行结果

 

 

 

 

① 首先解释ls -lai命令:

-l 除文件名称外,亦将文件型态、权限、拥有者、文件大小等资讯详细列出

-a 显示所有文件及目录 (ls内定将文件名或目录名称开头为"."的视为隐藏档,不会列出)

-i  显示文件索引节点号(inode number)

②再解释下argc、argv的具体含义 :
        argc和argv参数在用命令行编译程序时有用。main( int argc, char* argv[], char **env ) 中 
        第一个参数,int型的argc,为整型,用来统计程序运行时发送给main函数的命令行参数的个数,在VS中默认值为1。 
        第二个参数,char*型的argv[],为字符串数组,用来存放指向的字符串参数的指针数组,每一个元素指向一个参数。各成员含义如下: 
        argv[0]指向程序运行的全路径名 
        argv[1]指向在DOS命令行中执行程序名后的第一个字符串 
        argv[2]指向执行程序名后的第二个字符串 
        argv[3]指向执行程序名后的第三个字符串 
        argv[argc]为NULL 
        第三个参数,char**型的env,为字符串数组。env[]的每一个元素都包含ENVVAR=value形式的字符串,其中ENVVAR为环境变量,value为其对应的值。平时使用到的比较少。

从左到右解释含义


③输出文件索引节点号(inode number)

 

 

 

④判断文件的属性:

对文件属性进行逐一判断,符合就修改

 

 

⑤输出文件连接数

⑥输出文件所有者

通过文件对应的组输出文件所有者

 

 

⑦输出文件大小

直接输出stat结构体里面的这个

 

 

⑧输出时间

 

 也是通过stat结构体里面的st_mtime获取

⑨输出文件名

 

 直接打印struct dirent结构体里面的d_name即可

 

总结:Linux系统有很多实用的系统函数,比如用到的stat函数,能够极度方便我们解决这些问题,还有dirent.h是用于目录操作的头文件,linux 默认在/usr/include目录下。

思路就是先获取了当前的工作目录,然后再把文件信息读取到上述系统函数里面,接下来就很简单了

 

3. 通过该实验产生新的疑问及解答

对比了一下系统命令ls -lai

 

 我发现系统输出的是按照文件名称排序的,所以还需要一个排序算法

 

posted @ 2020-04-30 20:56  Amazing_C  阅读(218)  评论(0编辑  收藏  举报