当前位置:   article > 正文

Unix/Linux编程:获取文件状态------stat()_linux stat函数

linux stat函数

获取文件状态

在linux的shell解释器中,输入这样的命令stat + 文件名即可获取文件的属性。如:
在这里插入图片描述

我们可以通过以下函数获取上面的值

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



int fstat(int fildes, struct stat *buf);

int lstat(const char *pathname, struct stat *buf);

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

int fstat64(int fildes, struct stat64 *buf);

int lstat64(const char *pathname, struct stat64 *buf);

int stat64(const char *pathname, struct stat64 *buf);


struct stat {
    dev_t     st_dev;      文件的设备编号
    ino_t     st_ino;      节点
    mode_t    st_mode;     文件的类型和权限
    nlink_t   st_nlink;    连到该文件的硬链接数目,刚建立的文件值为1
    uid_t     st_uid;      用户ID
    gid_t     st_gid;      组ID
    dev_t     st_rdev;     设备类型)若此文件为设备文件,则为其设备编号
    off_t     st_size;     文件字节数(文件大小)
    blksize_t st_blksize;  块大小(文件I/O缓冲区的大小)
    blkcnt_t  st_blocks;   块数
    time_t    st_atime;    最后一次访问时间
    time_t    st_mtime;    最后一次修改时间
    time_t    st_ctime;    最后一次改变时间(指属性)
};



返回值:

	是成功是返回0,否则返回-1,并设置对应的errno

常见的可能出现的errno有:
  
       EACCES:目标文件所在的目录或某个上级目录没有查找权限

       EBADF:指定的文件描述符无效.

       EFAULT:无效的文件地址

       ELOOP:可能遇到了循环引用的软链接文件

       ENAMETOOLONG:文件路径名太长.

       ENOENT:目录或文件不存在

       ENOMEM:内核内存耗尽

       ENOTDIR:指定的文件路径上,某个部分不是目录

       EOVERFLOW:引用的文件太大了,或者使用的索引节点太多了,或者占用的存储块太多了。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • stat()会返回所命名文件的相关信息
  • lstat函数的形参跟stat函数的形参一样。其功能也跟stat函数功能一样,仅有一点不同:
    • stat函数是穿透(追踪)函数,即对软链接文件进行操作时,操作的是链接到的那一个文件,不是软链接文件本身;
    • 而lstat函数是不穿透(不追踪)函数,对软链接文件进行操作时,操作的是软链接文件本身。
  • fstat()则会返回由某个打开文件描述符所指代文件的相关信息

系统调用 stat()和 lstat()无需对其所操作的文件本身拥有任何权限,但针对指pathname 的父目录要有执行(搜索)权限。而只要供之以有效的文件描述符,fstat()系统调用总是成功

在这里插入图片描述
磁盘上的文件由很多属性,比如文件大小,文件属组等。如果要得到文件属性,进程可以定义一个结构struct stat,然后调用stat,告诉内核将这个文件属性复制到这个结构中:

int main()
{
    struct stat st;//定义结构体变量,保存所获取的文件属性
    int res = stat("valgrind.sh", &st);
    if(res == -1)//获取文件属性失败,errno设置为合适的值
    {
        perror("stat fail");
    }{
		printf("文件大小:%ld     块数: %ld    文件I/O缓冲区块大小: %ld\n",st.st_size, st.st_blocks, st.st_blksize);
	}
    
 }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

设备ID和i节点号: st_dev、st_info

  • st_dev字段标识文件所驻留的设备
    • dev_t 类型记录了设备的主、辅 ID
    • 利用宏 major()和 minor(),可提取 dev_t 值的主、辅 ID
    • major()和 minor()所返回的整形值大小也随 UNIX 实现的不同而各不相同。为保证可移植性,打印时应总是将返回值强制转换为 long
  • st_info字段则包含了文件的i节点号

利用以上两者,可在所有文件系统中唯一标识某个文件。

文件所有权:st_uid、st_gid

st_uid 和 st_gid 字段分别标识文件的属主(用户 ID)和属组(组 ID)。

每个文件都有文件所有者,Unix通过uid和gid来表示文件所有者和文件所属组

  • 文件所有者:就是创建文件的用户。
    • 当用户创建文件时,内核把文件所有者设为运行程序的用户
    • 如果程序有set-user-ID位,那么新文件的文件所有者就是程序的文件所有者
  • 组:就是指向创建动作的用户所在的组。有些情况下,组会被设置为与父目录的组相同

可以通过chown来修改文件的所有者和组

文件类型及权限: st_mode

st_mode 字段内含有位掩码,起标识文件类型和指定文件权限的双重作用。

  • mode_t st_mode是一个16位的short类型,对应16个标志位,其组成如下:
    在这里插入图片描述
    这里用到了掩码技术。那什么是掩码呢?

字段的编码

把多种信息编码到一个整数的不同字段中是一种常用的技术,比如:

613-495-4209电话号码(区号-局号-线号)
127.102.33.100IP地址

如何读取被编码的值

怎么直到212-222-4444所对应的区号是212?

  • 方法1:只取前3位,然后同212比较
  • 方法2:把暂时不需要的地方置0,也就是说同212-000-0000比较(相与)

这种把不需要的地方置于0的技术就叫做掩码

使用掩码来解码得到的文件类型

文件类型的模式字段的第一个字节的前4位,可以通过掩码来讲其他部分置于0,从而得到类型的值

在< sys/stat.h>下有如下定义:

#if defined __USE_BSD || defined __USE_MISC || defined __USE_XOPEN
# define S_IFMT		__S_IFMT
# define S_IFDIR	__S_IFDIR
# define S_IFCHR	__S_IFCHR
# define S_IFBLK	__S_IFBLK
# define S_IFREG	__S_IFREG
# ifdef __S_IFIFO
#  define S_IFIFO	__S_IFIFO
# endif
# ifdef __S_IFLNK
#  define S_IFLNK	__S_IFLNK
# endif



#define	__S_IFMT	0170000	/* These bits determine file type.  */

/* File types.  */
#define	__S_IFDIR	0040000	/* Directory.  */
#define	__S_IFCHR	0020000	/* Character device.  */
#define	__S_IFBLK	0060000	/* Block device.  */
#define	__S_IFREG	0100000	/* Regular file.  */
#define	__S_IFIFO	0010000	/* FIFO.  */
#define	__S_IFLNK	0120000	/* Symbolic link.  */
#define	__S_IFSOCK	0140000	/* Socket.  */
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

举个例子,掩码__S_IFMT 的值为0170000 ,可以用来过滤出前四位表示的文件类型。__S_IFREG 就表示普通文件,也就是说:

if((statbuf.st_mode & S_IFMT) == S_IFREG){
	 printf("regular file\n");
}
  • 1
  • 2
  • 3

与常量 S_IFMT 相与(&),可从该字段中析取文件类型

更简单的方法使用< sys/stat.h> 中的宏来代替上述代码:


#define	__S_ISTYPE(mode, mask)	(((mode) & __S_IFMT) == (mask))

#define	S_ISDIR(mode)	 __S_ISTYPE((mode), __S_IFDIR)
#define	S_ISCHR(mode)	 __S_ISTYPE((mode), __S_IFCHR)
#define	S_ISBLK(mode)	 __S_ISTYPE((mode), __S_IFBLK)
#define	S_ISREG(mode)	 __S_ISTYPE((mode), __S_IFREG)
#ifdef __S_IFIFO
# define S_ISFIFO(mode)	 __S_ISTYPE((mode), __S_IFIFO)
#endif
#ifdef __S_IFLNK
# define S_ISLNK(mode)	 __S_ISTYPE((mode), __S_IFLNK)
#endif
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

也就是说:

if(S_ISREG(statbuf.st_mode)){
	printf("regular file\n");
}
  • 1
  • 2
  • 3

下表所列为全套文件类型宏(定义于< sys/stat.h>)。这些宏均由 SUSv3 定义,并为 Linux所支持。

常 量测 试 宏文 件 类 型
S_IFREGS_ISREG()常规文件
S_IFDIRS_ISDIR()目录
S_IFCHRS_ISCHR()字符设备
S_IFBLKS_ISBLK()块设备
S_IFIFOS_ISFIFO()FIFO 或管道
S_IFSOCKS_ISSOCK()套接字
S_IFLNKS_ISLNK()符号链接

因为调用 stat()时会循符号链接而直抵实际文件,所以只有在调用 lstat()时才有可能返回类型 S_IFLNK。

文件类型是在创建文件的时候建立的,比如系统调用creat可以创建一个普通文件,其他类型的文件比如目录、设备等,可以使用不同的函数创建

文件一经创建,就无法修改

文件大小st_size、已分配块st_blocks 、最优 I/O 块大小st_blksize

st_size

  • 对于常规文件,表示文件的字节数
  • 对于符号链接,则表示链接所指路径名的长度,以字节为单位
  • 对于共享内存对象,该字段则表示对象的大小

st_blocks

  • 分配给文件的总块数,块大小为 512 字节,其中包括了为指针块所分配的空间
  • 之所以选择 512 字节大小的块作为度量单位,有其历史原因——对于UNIX 所实现的任何文件系统而言,最小的块大小即为 512 字节
  • 更为现代的 UNIX 文件系统则使用更大尺寸的逻辑块。例如,对于 ext2 文件系统,取决于其逻辑块大小为 1024、2048 还是 4096 字节,st_blocks 的取值将总是 2、4、8 的倍数
  • SUSv3 并未定义度量 st_blocks 时所使用的单位,故而 UNIX 实现可以不使用 512 字节作为其单位。大多数 UNIX 实现使用 512 字节作为 st_blocks 字段的单位,但 HP-UX 11 所使用的单位则视文件系统而定(有时为 1024 字节)
  • st_blocks 字段记录了实际分配给文件的磁盘块数量。如果文件内含空洞,该值将小于从相应文件字节数字段(st_size)的值。(执行显示磁盘使用情况的 du –k file 命令,便可获悉分配给文件的实际空间,单位为 KB。亦即,得自对文件 st_blocks 值,而非 st_size值的计算结果。

st_blksize

  • 针对文件系统上文件进行 I/O 操作时的最优块大小(以字节为单位)。
  • 若 I/O 所采用的块大小小于该值,则被视为低效。
  • 一般而言,st_blksize 的返回值为 4096、
    /**
        * 根据文件名取得该文件的大小
        * @param filename {const char*} 文件名
        * @return {acl_int64} >= 0: ok;  -1: error
        */
    acl_int64 acl_file::acl_file_size(const char *filename){
        struct stat sbuf;

        if (stat(filename, &sbuf) == -1) {
            return -1;
        }
        return sbuf.st_size;
    }

        /**
        * 根据文件句柄取得该文件的大小
        * @param fp {ACL_VSTREAM*} 对应的文件流句柄, 可以为空
        * @param arg {void*} 用户传递的参数, 以回调方式使用时此参数有效
        * @return {acl_int64} >= 0: ok;  -1: error
        */
    acl_int64 acl_file::acl_file_fsize(ACL_VSTREAM *fp, void *arg){
        struct stat sbuf;

        if (fstat(file_handle_, &sbuf) == -1) {
            return -1;
        }
        return sbuf.st_size;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

文件时间戳

st_atime、st_mtime 和 st_ctime 字段,分别记录了对文件的上次访问时间、上次修改时间,以及文件状态发生改变的上次时间。这 3 个字段的类型均属 time_t,是标准的 UNIX 时间格式,记录了自 Epoch 以来的秒数


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

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

int main()
{
    struct stat st;//定义结构体变量,保存所获取的文件属性
    int res = stat("/home/oceanstar/CLionProjects/c/build/valgrind.sh", &st);
    if(res == -1)//获取文件属性失败,errno设置为合适的值
    {
        perror("stat fail");
        return (1);
    }
    printf("文件大小:%ld     块数: %ld    文件I/O缓冲区块大小: %ld\n",st.st_size, st.st_blocks, st.st_blksize);
    printf("设备编号:%lu     Inode:%lu     硬链接:%lu\n ",st.st_dev, st.st_ino, st.st_nlink);
    printf("权限:%u , 用户ID:%d   组ID:%d    \n ",st.st_mode, st.st_uid, st.st_gid);

    switch (st.st_mode & S_IFMT) {
        case S_IFBLK:  printf("block device\n");            break;
        case S_IFCHR:  printf("character device\n");        break;
        case S_IFDIR:  printf("directory\n");               break;
        case S_IFIFO:  printf("FIFO/pipe\n");               break;
        case S_IFLNK:  printf("symlink\n");                 break;
        case S_IFREG:  printf("regular file\n");            break;
        case S_IFSOCK: printf("socket\n");                  break;
        default:       printf("unknown?\n");                break;
    }

    printf("Last status change:       %s", ctime(&st.st_ctime));
    printf("Last file access:         %s", ctime(&st.st_atime));
    printf("Last file modification:   %s", ctime(&st.st_mtime));


    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
#include <sys/sysmacros.h>
#if defined(_AIX)
#define _BSD
#endif
#if defined(__sgi) || defined(__sun)            /* Some systems need this */
#include <sys/mkdev.h>                          /* To get major() and minor() */
#endif
#if defined(__hpux)                             /* Other systems need this */
#include <sys/mknod.h>
#endif
#include <sys/stat.h>
#include <time.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <stdio.h>
#include <sys/types.h>

#define FP_SPECIAL 1            /* Include set-user-ID, set-group-ID, and sticky
                                   bit information in returned string */
#define STR_SIZE sizeof("rwxrwxrwx")
char *          /* Return ls(1)-style string for file permissions mask */
filePermStr(mode_t perm, int flags)
{
    static char str[STR_SIZE];

    /* If FP_SPECIAL was specified, we emulate the trickery of ls(1) in
       returning set-user-ID, set-group-ID, and sticky bit information in
       the user/group/other execute fields. This is made more complex by
       the fact that the case of the character displayed for this bits
       depends on whether the corresponding execute bit is on or off. */

    snprintf(str, STR_SIZE, "%c%c%c%c%c%c%c%c%c",
             (perm & S_IRUSR) ? 'r' : '-', (perm & S_IWUSR) ? 'w' : '-',
             (perm & S_IXUSR) ?
             (((perm & S_ISUID) && (flags & FP_SPECIAL)) ? 's' : 'x') :
             (((perm & S_ISUID) && (flags & FP_SPECIAL)) ? 'S' : '-'),
             (perm & S_IRGRP) ? 'r' : '-', (perm & S_IWGRP) ? 'w' : '-',
             (perm & S_IXGRP) ?
             (((perm & S_ISGID) && (flags & FP_SPECIAL)) ? 's' : 'x') :
             (((perm & S_ISGID) && (flags & FP_SPECIAL)) ? 'S' : '-'),
             (perm & S_IROTH) ? 'r' : '-', (perm & S_IWOTH) ? 'w' : '-',
             (perm & S_IXOTH) ?
             (((perm & S_ISVTX) && (flags & FP_SPECIAL)) ? 't' : 'x') :
             (((perm & S_ISVTX) && (flags & FP_SPECIAL)) ? 'T' : '-'));

    return str;
}
static void displayStatInfo(const struct stat *sb)
{
    printf("File type:                ");

    switch (sb->st_mode & S_IFMT) {
        case S_IFREG:  printf("regular file\n");            break;
        case S_IFDIR:  printf("directory\n");               break;
        case S_IFCHR:  printf("character device\n");        break;
        case S_IFBLK:  printf("block device\n");            break;
        case S_IFLNK:  printf("symbolic (soft) link\n");    break;
        case S_IFIFO:  printf("FIFO or pipe\n");            break;
        case S_IFSOCK: printf("socket\n");                  break;
        default:       printf("unknown file type?\n");      break;
    }

    printf("Device containing i-node: major=%ld   minor=%ld\n",
           (long) major(sb->st_dev), (long) minor(sb->st_dev));

    printf("I-node number:            %ld\n", (long) sb->st_ino);

    printf("Mode:                     %lo (%s)\n",
           (unsigned long) sb->st_mode, filePermStr(sb->st_mode, 0));

    if (sb->st_mode & (S_ISUID | S_ISGID | S_ISVTX))
        printf("    special bits set:     %s%s%s\n",
               (sb->st_mode & S_ISUID) ? "set-UID " : "",
               (sb->st_mode & S_ISGID) ? "set-GID " : "",
               (sb->st_mode & S_ISVTX) ? "sticky " : "");

    printf("Number of (hard) links:   %ld\n", (long) sb->st_nlink);

    printf("Ownership:                UID=%ld   GID=%ld\n",
           (long) sb->st_uid, (long) sb->st_gid);

    if (S_ISCHR(sb->st_mode) || S_ISBLK(sb->st_mode))
        printf("Device number (st_rdev):  major=%ld; minor=%ld\n",
               (long) major(sb->st_rdev), (long) minor(sb->st_rdev));

    printf("File size:                %lld bytes\n", (long long) sb->st_size);
    printf("Optimal I/O block size:   %ld bytes\n", (long) sb->st_blksize);
    printf("512B blocks allocated:    %lld\n", (long long) sb->st_blocks);

    printf("Last file access:         %s", ctime(&sb->st_atime));
    printf("Last file modification:   %s", ctime(&sb->st_mtime));
    printf("Last status change:       %s", ctime(&sb->st_ctime));
}
int
main(int argc, char *argv[])
{
    struct stat sb;
    bool statLink;           /* True if "-l" specified (i.e., use lstat) */
    int fname;                  /* Location of filename argument in argv[] */

    statLink = (argc > 1) && strcmp(argv[1], "-l") == 0;
    /* Simple parsing for "-l" */
    fname = statLink ? 2 : 1;

    if (fname >= argc || (argc > 1 && strcmp(argv[1], "--help") == 0)){
        printf("%s [-l] file\n"
               "        -l = use lstat() instead of stat()\n", argv[0]);
        exit(EXIT_FAILURE);
    }
    if (statLink) {
        if (lstat(argv[fname], &sb) == -1) {
            perror("lstat");
            exit(EXIT_FAILURE);
        }
    } else {
        if (stat(argv[fname], &sb) == -1){
            perror("stat");
            exit(EXIT_FAILURE);
        }
    }

    displayStatInfo(&sb);

    exit(EXIT_SUCCESS);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127

第二个例子:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
int main (int argc, char**argv)
{
    struct stat s;
    char *type, *readok;

    stat(argv[1], &s);
    if (S_ISREG(s.st_mode)){
        type= "regular";
    }else if(S_ISDIR(s.st_mode)){
        type= "directory";
    }else{
        type= "other";
    }

    if ((s.st_mode & S_IRUSR)){
        readok = "yes";
    }else{
        readok = "no";
    }

    printf("type: %s, read: %s\n", type, readok);

    return(0);
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/菜鸟追梦旅行/article/detail/523129
推荐阅读
相关标签
  

闽ICP备14008679号