Unix编程实践教程笔记(三) 目录与文件属性(写一个ls命令)

3. 目录与文件属性(写一个ls命令)

ls找出当前目录文件名,ls -l显示详细信息(文件名字典序排序后)

image

image

image

3.1 文件树

磁盘上的文件和目录被unix组织为一颗目录树

每个节点为目录或文件

目录是特殊的文件,包含许多文件记录

.表示当前目录,..表示上级目录

more,cat等命令可以区分文件和目录,且目录文件包含一定的数据结构

image

#include <dirent.h>

   struct dirent *readdir(DIR *dirp);

   int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result);
   
   DIR *opendir(const char *name);
   
              struct dirent {
               ino_t          d_ino;       /* inode num
ber */
               off_t          d_off;       /* not an offset; see NOTES */
               unsigned short d_reclen;    /* length of this record */
               unsigned char  d_type;      /* type of file; not supported
                                              by all filesystem types */
               char           d_name[256]; /* filename */
           };


/*
SEE ALSO
       open(2),  closedir(3),  dirfd(3),  readdir(3),
       rewinddir(3),     scandir(3),      seekdir(3),
       telldir(3)
       
       */

d_name存储文件名

3.2 编写ls

通过opendir打开目录,返回目录,将其传入readdir,利用循环不断的读出描述文件属性的结构体,打印出其中的d_name

#include<stdio.h>
#include<unistd.h>
#include<fcntl.h>
#include<stdlib.h>
#include<dirent.h>
void do_ls(char dirname[])
{
    //用于保存指向目录的指针
    DIR* dir_ptr;
    //用于保存文件信息
    struct dirent * direntp;
    //打开目录
    if((dir_ptr=opendir(dirname))==NULL)
    {
        fprintf(stderr,"myls:cannot open %s\n",dirname);//打开失败
    }
    else{
        //不断的循环,直到读不到文件属性
        while((direntp=readdir(dir_ptr))!=NULL)
        {
            //输出文件名
            printf("%s   ",direntp->d_name);
        }
        closedir(dir_ptr);
    }
}

int main(int argc,char* argv[])
{   
    if(argc==1)
    {
        do_ls(".");//检索当前目录
    }
    else{
        while(--argc)
        {
            printf("%s\n",*++argv);
            do_ls(*argv);
        }
    }
    return 0;
}

image

不足:
未排序输出,输出了隐藏文件.

没有选型-l,无法输出文件的详细信息,这些信息不是在目录中存储的

3.3 完善,编写ls -l

ls -l的输出:

image

image

image

提取文件状态信息的函数

#include <sys/types.h>
   #include <sys/stat.h>
   #include <unistd.h>

   int stat(const char *pathname, struct stat *buf);
//将信息放在buf中,成功返回 0,失败返回-1


           struct stat {
               dev_t     st_dev;         /* ID of device containing fi
le */
               ino_t     st_ino;         /* inode number */
               mode_t    st_mode;        /* protection */
               nlink_t   st_nlink;       /* number of hard links */
               uid_t     st_uid;         /* user ID of owner */
               gid_t     st_gid;         /* group ID of owner */
               dev_t     st_rdev;        /* device ID (if special file
) */
               off_t     st_size;        /* total size, in bytes */
               blksize_t st_blksize;     /* blocksize for filesystem I
/O */
               blkcnt_t  st_blocks;      /* number of 512B blocks allo
cated */

               /* Since Linux 2.6, the kernel supports nanosecond
                  precision for the following timestamp fields.
                  For the details before Linux 2.6, see NOTES. */

               struct timespec st_atim;  /* time of last access */
               struct timespec st_mtim;  /* time of last modification */
               struct timespec st_ctim;  /* time of last status change */

           #define st_atime st_atim.tv_sec      /* Backward compatibility */
           #define st_mtime st_mtim.tv_sec
           #define st_ctime st_ctim.tv_sec
           };
例如:使用stat获取并输出某文件的大小
#include<stdio.h>
#include<unistd.h>
#include<fcntl.h>
#include<stdlib.h>
#include<dirent.h>
#include<sys/stat.h>

int main()
{
    struct stat buf;
    if(stat("/etc/passwd",&buf)==-1)
    {
        printf("faled\n");
    }
    else
    {
        printf("size:%d\n",buf.st_size);
    }
    return 0;
}

image

由于结构体中的user和group字段都是数值,且mode也为数值,所以需要将其转换为响应的字符

image

模式字段

前4位:文件类型

三位文件特殊属性

九位许可权限:分别对应三组:文件所有者,同组用户和其他用户

每组三位:读,写,执行,1为有,0为无权限

掩码:

将不需要的字段置为0,需要的字段值不发生变化

整数是bit组成的序列

215 = 0 1 1 0 1 0 1 1 1

掩码技术

与0做&操作可以将对应的bit位置为0

使用八进制数

使用掩码得到文件类型

image

通过使用掩码将其他无关部分置为0,再与文件类型对应的代码比较,从而可以判断文件类型

使用宏简化操作

//使用宏来与目录的mode代码比较(将无关的部分置为0),判断文件类型
#define S_ISFIFO(m) (((m)&(0170000))==(0010000))
#define S_ISDIR(m) (((m)&(0170000))==(0040000))
#define S_ISCHR(m) (((m)&(0170000))==(0020000))
#define S_ISBLK(m) (((m)&(0170000))==(0060000))
#define S_ISREG(m) (((m)&(0170000))==(0100000))

通过解码来获取权限情况

最低的九位在<sys/stat.h>中每一位都有对应的掩码

image

将用户/组ID转换为字符串

struct stat中的用户和组是以ID形式存在,但是ls要求输出相应的字符串(名字)

/etc/passwd包含了用户列表,所有的用户都对此文件有读权限,但是不一定所有用户多保存在此文件中

还可能保存在NIS中(一台大家都能访问到的主机)

image

组ID处理

用户要分为不同的组,/etc/group保存所有组信息

passwd文件中的组信息,是用户的主组,用户还可以是其他组的成员

image

使用

#include <sys/types.h>
       #include <grp.h>

       struct group *getgrnam(const char *name);

       struct group *getgrgid(gid_t gid);

           struct group {
               char   *gr_name;        /* group name */
               char   *gr_passwd;      /* group password */
               gid_t   gr_gid;         /* group ID */
               char  **gr_mem;         /* NULL-terminated array of poi
nters
                                          to names of group members */
           };
//以下函数可以取得完整的用户列表
       #include <sys/types.h>
       #include <pwd.h>

       struct passwd *getpwnam(const char *name);

       struct passwd *getpwuid(uid_t uid);

  struct passwd {
               char   *pw_name;       /* username */
               char   *pw_passwd;     /* user password */
               uid_t   pw_uid;        /* user ID */
               gid_t   pw_gid;        /* group ID */
               char   *pw_gecos;      /* user information */
               char   *pw_dir;        /* home directory */
               char   *pw_shell;      /* shell program */
           };

如果用户信息保存在etc/passwd,则会去查找passwd,如果在NIS,则会去查找NIS

如果uid不合法,则会返回一个空指针NULL,这样就没意义了,下面在代码实现时会说到如何解决

UID没有对应的用户名的情况

image

3.4 设置和修改文件的属性

一旦文件创建,则其类型就不能修改

文件类型有:普通文件,设备文件,目录文件等

每个文件都有9位许可权限和3位特殊属性,创建文件后,它们可以被chmod系统调用修改

3.4.1 文件类型

文件一旦创建,类型不可改变,creat创建的是普通文件

3.4.2 许可位,特殊属性位

umask:

#include <sys/types.h>
       #include <sys/stat.h>

       mode_t umask(mode_t mask);

即使creat请求了文件的许可位,但是内核也不一定会照做,因为内核会通过新建文件掩码来设置:

umask,指定哪些位需要被关掉

chmod系统调用:

#include <sys/stat.h>

       int chmod(const char *pathname, mode_t mode);
		
	S_ISUID  (04000)  set-user-ID  (set process effective user ID
                         on execve(2))

       S_ISGID  (02000)  set-group-ID (set process  effective  group
                         ID  on  execve(2);  mandatory  locking,  as
                         described in fcntl(2); take  a  new  file's
                         group  from  parent directory, as described
                         in chown(2) and mkdir(2))

       S_ISVTX  (01000)  sticky bit (restricted  deletion  flag,  as
                         described in unlink(2))

       S_IRUSR  (00400)  read by owner

       S_IWUSR  (00200)  write by owner

       S_IXUSR  (00100)  execute/search  by  owner ("search" applies
                         for directories,  and  means  that  entries
                         within the directory can be accessed)

       S_IRGRP  (00040)  read by group

       S_IWGRP  (00020)  write by group

       S_IXGRP  (00010)  execute/search by group

       S_IROTH  (00004)  read by others

       S_IWOTH  (00002)  write by others

       S_IXOTH  (00001)  execute/search by others

此调用可以接收八进制,也可以接收sys/stat.h中定义的值

image

3.4.3 文件链接数

文件被引用的次数(别名的数量)

3.4.4 文件所有者与组

通过组ID和用户ID来标识文件按所有者和所属的组

image

#include <unistd.h>

   int chown(const char *pathname, uid_t owner, gid_t group);//后两位传入-1则不会改变

文件所有者可以将文件的组改为任何一个他所属的组

文件大小:creat时,可以设置为0,但是不存在能直接减小文件战用空间的函数

3.4.5 文件时间

最后修改时间,最后访问时间,属性最后修改时间

内核会自动修改这三个时间,也可以直接用系统调用来修改:
#include <sys/types.h>
#include <utime.h>

int utime(const char *filename, const struct utimbuf *times);
        struct utimbuf {
               time_t actime;       /* access time */
               time_t modtime;      /* modification time */
           };

可以使用touch命令修改最后访问和最后修改时间

3.4.6 文件名

命令mv改变文件名

//修改文件名或移动文件位置
       #include <stdio.h>

       int rename(const char *oldpath, const char *newpath);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值