文件I/O部分断断续续写了三天,最后总结发现还有好多内容是略过没讲的,我的内心是崩溃的。UNIX环境高级编程这本书,虽然我只看了四章我就发现了书里面的内容讲的太跳,如果是刚接触UNIX或者没有一点C语言基础的会很难上手。这就造成了,前面讲的会漏掉很多内容。

一、函数 stat、fstat、fstatat 和 lstat

详细内容可 man fstat 查看

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>int stat(const char *path, struct stat *buf);
int fstat(int fd, struct stat *buf);
int lstat(const char *path, struct stat *buf);

1、参数解析

第一个参数:文件路径/文件描述符
第二个参数:结构体指针,结构体变量的地址。结构体基本形式如下:
struct stat {dev_t     st_dev;     /* ID of device containing file */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 file system I/O */blkcnt_t  st_blocks;  /* number of 512B blocks allocated */time_t    st_atime;   /* time of last access */time_t    st_mtime;   /* time of last modification */time_t    st_ctime;   /* time of last status change */};

其中时间函数部分,参看:C语言再学习 -- 时间函数

2、返回值

成功返回 0, 失败返回 -1

3、函数功能

获取指定文件的状态信息

4、示例说明

#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>int main (void)
{struct stat st = {};int res = stat ("a.txt", &st);if (-1 == res)perror ("Fail to stat"), exit (1);printf ("st_mode = %o, st_size = %ld, st_mtime = %ld\n", st.st_mode, st.st_size, st.st_mtime);printf ("文件的权限是:%o\n", st.st_mode&0777);printf ("文件的大小是:%ld\n", st.st_size);if (S_ISREG (st.st_mode))printf ("是普通文件\n");if (S_ISDIR (st.st_mode))printf ("是目录文件\n");printf ("最后一次修改时间是:%s\n", ctime (&st.st_mtime));struct tm* pt = localtime (&st.st_mtime);printf("最后一次修改时间是:%d-%d-%d %02d:%02d:%02d\n",pt->tm_year+1900,pt->tm_mon+1,pt->tm_mday,pt->tm_hour,pt->tm_min,pt->tm_sec);return 0;
}
输出结果:
st_mode = 100644, st_size = 0, st_mtime = 1490857501
文件的权限是:644
文件的大小是:0
是普通文件
最后一次修改时间是:Thu Mar 30 15:05:01 2017最后一次修改时间是:2017-3-30 15:05:01

5、总结

这部分只讲函数 stat,其实在 Linux 下 stat 命令 也可以实现这些功能了。 参看:stat 命令

# stat  c_test/文件:"c_test/"大小:4096          块:8          IO 块:4096   目录
设备:801h/2049d    Inode:2102110     硬链接:2
权限:(0775/drwxrwxr-x)  Uid:( 1000/  tarena)   Gid:( 1000/  tarena)
最近访问:2017-03-30 15:05:03.502154419 +0800
最近更改:2017-03-30 15:05:01.858151879 +0800
最近改动:2017-03-30 15:05:01.858151879 +0800
创建时间:-# stat  a.txt 文件:"a.txt"大小:0           块:0          IO 块:4096   普通空文件
设备:801h/2049d    Inode:2121684     硬链接:1
权限:(0644/-rw-r--r--)  Uid:(    0/    root)   Gid:(    0/    root)
最近访问:2017-03-30 15:05:01.858151879 +0800
最近更改:2017-03-30 15:05:01.858151879 +0800
最近改动:2017-03-30 15:05:01.858151879 +0800
创建时间:-

二、文件类型

UNIX 系统的大多数文件是普通文件或目录,但是也有另外一些文件类型。文件类型包括如下几种.

1、普通文件

在 UNIX/Linux 系统中通常所见到的文件,如用 C/C++ 语言编写的源代码文件,编译器、汇编器和连接器产生的汇编文件、目标文件和可执行文件,各种系统配置文件,shell 脚本文件,包括音频、视频等数字内容的多媒体文件,乃至包括数据库在内的各种应用程序所维护、管理和使用的数据文件等,都是普通文件。
一个文件包括两部分数据,一部分是元数据,如文件的类型、权限、大小、用户、组、各种时间戳等,存储在 i 节点中,另一部分是内容数据,存储在数据块中

2、目录文件

系统通过 i 节点唯一地标识一个文件的存在,但人们更愿意使用有意义的文件名来访问文件,目录就是用来建立文件名和 i 节点之间的映射的。
目录的本质就是一个普通文件,与其它普通文件唯一的区别就是它仅仅存储文件名和 i 节点号的映射,每一个这样的映射,用目录中的一个条目表示,谓之硬链接。
介绍相对路径和绝对路径:
Lniux的文件系统中有一个大分组,它包含了文件系统中所有文件,这个大的分组用一个专门的目录表示,这个目录叫做根目录,根目录可以使用“/”表示
路径可以用来表示文件或者文件夹所在的位置,路径是从一个文件夹开始走到另一个文件夹或者文件位置中间的这条路。把这条路经过的所有文件夹名称按顺序书写出来的结果就可以表示这条路。
路径分为绝对路径和相对路径
绝对路径:
起点必须是根目录,如 /abc/def  所有绝对路径一定是以“/”作为开头的
相对路径:可以把任何一个目录作为起点,如../../abc/def  相对路径编写时不应该包含起点位置
相对目录中“..”表示上层目录
相对路径中用“.”表示当前
终端窗口里的当前目录是所有相对路径的起点,当前目录的位置是可以修改的。
pwd 命令:可以用来查看当前目录的位置
cd  命令:可以用来修改当前目录位置   
ls  命令:可以用来查看一个目录的内容

3、块特殊文件

这种类型的文件提供对设备(如磁盘)带缓冲的访问,每次访问以固定长度为单位进行。

4、字符特殊文件

这种类型的文件提供对设备不带缓冲的访问,每次访问长度可变。系统中的所有设备要么是字符特殊文件,要么是块特殊文件。

5、FIFO

这种类型的文件用于进程间通信,有时也称为命名管道。

6、套接字

这种类型的文件用于进程间的网络通信。套接字也可用于一台宿主机上进程之间的非网络通信。

7、符号链接

这种类型的文件指向另一个文件。
再讲到 stat 函数时,其结构中的 st_mode 有各类文件类型的信息
S_ISREG(m)  is it a regular file  (普通文件)
S_ISDIR(m)  directory  (目录文件)
S_ISCHR(m)  character device  (字符特殊文件)
S_ISBLK(m)  block device  (块特殊文件)
S_ISFIFO(m) FIFO (named pipe)  (管道或 FIFO)
S_ISLNK(m)  symbolic link (符号链接)(Not in POSIX.1-1996.)
S_ISSOCK(m) socket (套接字)(Not in POSIX.1-1996.)

我们讲权限的时候,也讲过使用 ls -l 命令 也可以查看文件的类型的:

# ls -l
总用量 40
drwxr-xr-x 2 root   root    4096 Mar 30 16:42 test
-rw-r--r-- 1 root   root     872 Mar 30 15:04 test.c

其中第一个字符含义:
-  普通文件  (例如:/etc/passwd)
d  目录  (例如:/etc)
s  本地套接字  (例如:/dev/log)
c  字符设备  (例如:/dev/tty)
b  块设备  (例如:/dev/sr0)
l  符号链接  (例如:/dev/cdrom)
p  有名管道/FIFO  (例如:/var/lib/oprofile/opd_pipe)

相对于用函数,我更喜欢用指令来获取文件类型
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>int main(int argc, char *argv[])
{struct stat sb;if (argc != 2) {fprintf(stderr, "Usage: %s <pathname>\n", argv[0]);exit(EXIT_FAILURE);}if (stat(argv[1], &sb) == -1) {perror("stat");exit(EXIT_FAILURE);}printf("File type:                ");switch (sb.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("I-node number:            %ld\n", (long) sb.st_ino);printf("Mode:                     %lo (octal)\n",(unsigned long) sb.st_mode);printf("Link count:               %ld\n", (long) sb.st_nlink);printf("Ownership:                UID=%ld   GID=%ld\n",(long) sb.st_uid, (long) sb.st_gid);printf("Preferred I/O block size: %ld bytes\n",(long) sb.st_blksize);printf("File size:                %lld bytes\n",(long long) sb.st_size);printf("Blocks allocated:         %lld\n",(long long) sb.st_blocks);printf("Last status change:       %s", ctime(&sb.st_ctime));printf("Last file access:         %s", ctime(&sb.st_atime));printf("Last file modification:   %s", ctime(&sb.st_mtime));exit(EXIT_SUCCESS);
}
输出结果:
# ./a.out test.c
File type:                regular file
I-node number:            2121694
Mode:                     100644 (octal)
Link count:               1
Ownership:                UID=0   GID=0
Preferred I/O block size: 4096 bytes
File size:                1642 bytes
Blocks allocated:         8
Last status change:       Fri Mar 31 09:15:48 2017
Last file access:         Fri Mar 31 09:15:53 2017
Last file modification:   Fri Mar 31 09:15:48 2017
# ls -l /dev/tty
crw-rw-rw- 1 root tty 5, 0 Mar 29 16:00 /dev/tty
# stat /dev/tty文件:"/dev/tty"大小:0            块:0          IO 块:4096   字符特殊文件
设备:5h/5d Inode:4961        硬链接:1     设备类型:5,0
权限:(0666/crw-rw-rw-)  Uid:(    0/    root)   Gid:(    5/     tty)
最近访问:2017-03-30 16:35:58.445938548 +0800
最近更改:2017-03-29 16:00:37.867616181 +0800
最近改动:2017-03-29 16:00:37.867616181 +0800
创建时间:-

三、文件访问权限

当提及 文件 时,指的是前面所提到的任何类型的文件。所有文件类型(目录、字符特别文件等)都是有访问权限的
st_mode 值也包含了对文件的访问权限位:
   The following flags are defined for the st_mode field:S_IFMT     0170000   bit mask for the file type bit fieldsS_IFSOCK   0140000   socketS_IFLNK    0120000   symbolic linkS_IFREG    0100000   regular fileS_IFBLK    0060000   block deviceS_IFDIR    0040000   directoryS_IFCHR    0020000   character deviceS_IFIFO    0010000   FIFOS_ISUID    0004000   set UID bitS_ISGID    0002000   set-group-ID bit (see below)S_ISVTX    0001000   sticky bit (see below)S_IRWXU    00700     mask for file owner permissions S_IRUSR    00400     owner has read permissionS_IWUSR    00200     owner has write permissionS_IXUSR    00100     owner has execute permissionS_IRWXG    00070     mask for group permissionsS_IRGRP    00040     group has read permissionS_IWGRP    00020     group has write permissionS_IXGRP    00010     group has execute permissionS_IRWXO    00007     mask for permissions for others (not in group)S_IROTH    00004     others have read permissionS_IWOTH    00002     others have write permissionS_IXOTH    00001     others have execute permission

chmod 命令用于修改这 9 个权限位。该命令允许我们用 u 表示用户(所有者),用 g 表示组,用 o 表示其他。

对于一个文件的读权限决定了我们是否能够打开现有文件进行读操作。这与 open 函数的 O_RDONLY 和 O_RDWR 标志有关。
对于一个文件的写权限决定了我们是否能够打开现有文件进行写操作。这与 open 函数的 O_WRONLY 和 O)RDWR 标志有关。
为了在 open 函数中对一个文件指定 O_TRUNC 标志,必须对该文件具有写权限(经测试不限于写权限)
为了在一个目录中创建一个新文件,必须对该目录具有写权限和执行权限
为了删除一个现有文件,必须对包含该文件的目录具有写权限和执行权限。对该文件本身则不需要读、写权限。
如果用 7 个 exec 函数中的任何一个执行某个文件,都必须对该文件具有执行权限。该文件还必须是一个普通文件

四、函数 access

#include <unistd.h>
int access(const char *pathname, int mode);

1、参数解析

第一个参数:文件的路径和文件名

第二个参数:操作模式
F_OK:判断文件是否存在
R_OK:判断文件是否可读
W_OK:判断文件是否可写
X_OK:判断文件是否可执行
查看 /usr/include/unistd.h 可发现这几个模式可用数字表示

/* Values for the second argument to access.These may be OR'd together.  */
#define R_OK    4       /* Test for read permission.  */
#define W_OK    2       /* Test for write permission.  */
#define X_OK    1       /* Test for execute permission.  */
#define F_OK    0       /* Test for existence.  */

2、函数功能:

确定文件或文件夹的访问权限。即,检查某个文件的存取方式,比如说是只读方式、只写方式等。

3、返回值

如果指定的存取方式有效,则函数返回 0,否则函数返回 -1。

4、示例说明

#include <stdio.h>
#include <unistd.h>int main (void)
{if (0 == access ("a.txt", F_OK))printf ("文件存在\n");if (0 == access ("a.txt", 4))printf ("文件可读\n");if (0 == access ("a.txt", W_OK))printf ("文件可写\n");if (0 == access ("a.txt", 1))printf ("文件可执行\n");return 0;
}
输出结果:
文件存在
文件可读
文件可写

五、函数 umask

#include <sys/types.h>
#include <sys/stat.h>
mode_t umask(mode_t mask);

1、函数功能

主要用于设置创建文件时需要屏蔽的权限,返回之前屏蔽的权限
 umask() 会将系统umask值设成参数 mask&0777 后的值,然后将先前的umask值返回。在使用 open() 建立新文件时,该参数 mode 并非真正建立文件的权限,而是 (mode& ~umask) 的权限值.
系统默认的屏蔽权限为 (0022):
//创建文件 hh 查看权限:
# ls -l hh
-rw-r--r-- 1 root root 0 Mar 31 10:34 hh
查看:
# umask
0022

2、示例说明

#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>int main (void)
{//使用umask函数设置屏蔽的权限mode_t old = umask (0055);//设置新的屏蔽字,返回旧的系统默认// 当前系统默认屏蔽 0055printf ("old = %o\n", old);int fd = open ("a.txt", O_RDWR | O_CREAT, 0777);if (-1 == fd)perror ("fail to open"),exit (1);//恢复系统默认的屏蔽字//对已经创建过的文件权限没有影响umask (old);close (fd);return 0;
}
输出结果:
old = 22查看 a.txt 权限,说明实际权限为 0722 (0777&~0055)屏蔽了0055
# ls -la a.txt
-rwx-w--w- 1 root root 0 Mar 31 10:25 a.txt

示例说明:

在建立文件时指定文件权限为0777,通常umask值默认为 0022,将 umask值新设为 0055,返回老的默认值 22;则该文件的真正权限则为0777&~055=0722,也就是rw-w--w--

3、Linux 下的umask 指令

参看:umask 命令

(1)选项

-p:输出的权限掩码可直接作为指令来执行;
-S:以符号方式输出权限掩码。

(2)示例

设置新屏蔽权限:
# umask -p 0055# umask -S 0055
u=rwx,g=w,o=w查看:
# umask
0055

六、函数 chmod 和 fchmod

#include <sys/stat.h>
int chmod(const char *path, mode_t mode);
int fchmod(int fd, mode_t mode);

1、参数解析

第一个参数:文件路径/文件描述符
第二个参数:权限模式
查看 man fchmod
The new file permissions are specified in mode, which is a bit mask created by ORing together zero or more of the following: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 newfile'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 ownerS_IWUSR  (00200)  write by ownerS_IXUSR  (00100)  execute/search  by  owner  ("search"  applies  for directories, and means that entries within the directory can beaccessed)S_IRGRP  (00040)  read by groupS_IWGRP  (00020)  write by groupS_IXGRP  (00010)  execute/search by groupS_IROTH  (00004)  read by othersS_IWOTH  (00002)  write by othersS_IXOTH  (00001)  execute/search by others

2、返回值

成功返回 0,失败返回 -1.

3、函数功能

修改现有文件的访问权限。
chmod 函数在指定的文件上进行操作,而 fchmod 函数则对已打开的文件进行操作
为了改变一个文件的权限位,进程的有效用户 ID 必须等于文件的所有者 ID,或者该进程必须具有超级用户权限

4、示例说明

//示例一 chmod
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>int main (void)
{struct stat st;int res = stat ("a.txt", &st);if (-1 == res)perror ("fail to stat"), exit (1);printf ("权限是:%o\n", st.st_mode&0777);res = chmod ("a.txt", 0600);if (-1 == res)perror ("fail to chmod"), exit (1);res = stat ("a.txt", &st);if (-1 == res)perror ("fail to stat"), exit (1);printf ("权限是:%o\n", st.st_mode&0777);return 0;
}
输出结果:
权限是:644
权限是:600
//示例二 fchmod
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>int main (void)
{int fd = open ("a.txt", O_RDONLY | O_CREAT, 0644);struct stat st;int res = stat ("a.txt", &st);if (-1 == res)perror ("fail to stat"), exit (1);printf ("权限是:%o\n", st.st_mode&0777);res = fchmod (fd, 0600);if (-1 == res)perror ("fail to chmod"), exit (1);res = stat ("a.txt", &st);if (-1 == res)perror ("fail to stat"), exit (1);printf ("权限是:%o\n", st.st_mode&0777);return 0;
}
输出结果:
权限是:644
权限是:600

5、示例总结

示例一:chmod 函数在指定的文件上进行操作。
示例二:fchmod 函数则对已打开的文件进行操作

6、Linux 下 chmod 命令

参看:C语言再学习 -- 修改linux文件权限

(1)修改文件 a.txt 权限为 0644

chmod 644 a.txt
查看:
# ls -l a.txt
-rw-r--r-- 1 tarena root 0 Mar 31 16:16 a.txt

七、函数 chown、fchown、lchown

#include <unistd.h>
int chown(const char *path, uid_t owner, gid_t group);
int fchown(int fd, uid_t owner, gid_t group);
int lchown(const char *path, uid_t owner, gid_t group);

1、参数解析

第一个参数:文件路径/文件描述符
第二个参数:用户 ID
第三个参数:组 ID

2、函数功能

可用于更改文件的用户 ID 和组 ID。
除了所引用的文件是符号链接以外,这三个函数的操作相似。在符号链接的情况下,lchown更改符号链接本身的所有者,而不是该符号链接所指向的文件
如若两个参数 owne r或 group 中的任意一个是 -1,则对应的 ID 不变。
基于 BSD 的系统一直规定只有超级用户才能更改一个文件的所有者。这样做的原因是防止用户改变其文件的所有者从而摆脱磁盘空间限额对他们的限制。系统 V 则允许任一用户更改他们所拥有的文件的所有者。

3、返回值

成功返回 0, 失败返回 -1.

4、示例说明

//示例一 chown
#include <stdio.h>
#include <unistd.h>  int main (void)
{  //chown ("a.txt", 0, -1);  chown ("a.txt", 0, 0);  return 0;
}
修改前:
# ls -l a.txt
总用量 20
-rw------- 1 tarena tarena    0 Mar 31 14:24 a.txt  chown ("a.txt", 0, 0); 修改后:
# ls -l a.txt
总用量 20
-rw------- 1 root   root      0 Mar 31 14:24 a.txt  chown ("a.txt", 0, -1); 修改后:
# ls -l a.txt
总用量 20
-rw------- 1 root   tarena      0 Mar 31 14:24 a.txt  
//示例二 fchown
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdlib.h>int main (void)
{int fd = open ("a.txt", O_RDONLY | O_CREAT, 0644);if (-1 == fd)perror ("fail to open"), exit (1); fchown (fd , 1000, 1000);return 0;
}
修改前:
# ls -la
总用量 12
-rw-r--r-- 1 root   root      0 Mar 31 15:34 a.txt修改后:
# ls -la
总用量 20
-rw-r--r-- 1 tarena tarena    0 Mar 31 15:34 a.txt
//示例三 lchown
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>int main (void)
{int res = lchown ("/dev/cdrom1", 1000, 1000);if (-1 == res)perror ("fail to lchown"), exit (1);return 0;
}
修改前:
# ls -l cdrom1 sr0
lrwxrwxrwx 1 root root      3 Mar 29 16:00 cdrom1 -> sr0
brw-rw---- 1 root cdrom 11, 0 Mar 29 16:00 sr0修改后:
# ls -l cdrom1 sr0
lrwxrwxrwx 1 tarena tarena     3 Mar 29 16:00 cdrom1 -> sr0
brw-rw---- 1 root   cdrom  11, 0 Mar 29 16:00 sr0

5、示例总结

示例一:我们查看 cat /etc/passwd 可以看到所有用户对应的 用户 ID 和 组 ID。
其中超级用户 root 的用户 ID 为 0,组 ID 也为 0。而我所用的普通用户 tarena 用户 ID 为 1000,组 ID 为 1000
如若两个参数 owne r或 group 中的任意一个是 -1,则对应的 ID 不变
# cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
tarena:x:1000:1000:tarena,,,:/home/tarena:/bin/bash

示例二:fchown,打开时文件描述符。

示例三:这里有个名词 符号链接,上面我们讲文件类型是有讲到。比如 /dev/cdrom1就是符号链接它所指向的文件为sr0。通过示例三可以验证,lchown 是改变符号链接本身的所有者,而不是该符号链接所指向的文件。
# stat /dev/cdrom1 文件:"/dev/cdrom1" -> "sr0"大小:3           块:0          IO 块:4096   符号链接
设备:5h/5d Inode:7459        硬链接:1
权限:(0777/lrwxrwxrwx)  Uid:(    0/    root)   Gid:(    0/    root)
最近访问:2017-03-31 15:59:53.712010169 +0800
最近更改:2017-03-29 16:00:36.411616237 +0800
最近改动:2017-03-31 15:59:47.936011311 +0800
创建时间:-

6、linux 下 chown 和 chgrp 命令

参看:C语言再学习 -- 修改linux文件权限

(1)将test目录所在的组由root修改为zslf组

sudo chgrp -R zslf test
查看:
root@zslf-:/mnt# ls -la test/
总用量 8
drwxr-xr-x 2 root zslf 4096 11月 24 17:20 .
drwxr-xr-x 5 root root 4096 11月 24 17:20 .. 

(2)将test目录下的所有文件和子目录的属主由root改为zslf

sudo chown -R zslf.zslf test
查看:
root@zslf-virtual-machine:/mnt# ls -la test/
总用量 8
drwxr-xr-x 2 zslf root 4096 11月 24 17:20 .
drwxr-xr-x 5 root root 4096 11月 24 17:20 ..  

(3)将test目录下的所有文件和子目录的属主由root改为zslf,属组有root改为zslf。

sudo chown -R zslf.zslf test
查看:
root@zslf-virtual-machine:/mnt# ls -la test/
总用量 8
drwxr-xr-x 2 zslf zslf 4096 11月 24 17:33 .
drwxr-xr-x 5 root root 4096 11月 24 17:20 ..
-rw-r--r-- 1 zslf zslf    0 11月 24 17:33 hh 

八、文件长度(大小)

stat 结构成员 st_size 表示以字节为单位的文件的长度。此字段只对普通文件、目录文件和符号链接有意义。
对于普通文件,其文件长度可以是 0,在开始读这种文件时,将得到文件结束(EOF)指示
参看:C语言再学习 -- EOF、feof函数、ferror函数  对于目录,文件长度通常是一个数(如 16 或 512)的整倍数。例如,下面的例子中,目录文件长度为 4096,普通文件 a.txt 文件长度可以是 0.

# ls -la
总用量 20
drwxrwxr-x 2 tarena tarena 4096 Mar 31 16:16 .
drwxrwxr-x 4 tarena tarena 4096 Mar 28 15:38 ..
-rwxr-xr-x 1 root   root   7233 Mar 31 16:16 a.out
-rw-r--r-- 1 tarena root      0 Mar 31 16:16 a.txt
-rw-r--r-- 1 root   root    184 Mar 31 16:15 test.c
对于符号链接,文件长度是在文件名中的实际字节数。注意,因为符号链接文件长度总是由 st_size 指示,所以它并不包含通常 C 语言用作名字结尾的 null 字节)。例如,在下面的例子中,符号链接 cdrom1 的文件长度为 3
# ls -l /dev/cdrom1
lrwxrwxrwx 1 root root 3 Apr  5 09:19 /dev/cdrom1 -> sr0

九、文件空洞

文件空洞,是由所设置的偏移量超过文件尾端,并写入了某些数据后造成的。位于文件中但没有被写过的字节都被设为 0,文件空洞不占用磁盘空间,但被计算在文件大小之内。

作为一个例子,考虑下列情况:
# ls -l a.out
-rwxr-xr-x 1 root root 7233 Mar 31 16:16 a.out# du -h a.out
8.0K    a.out

文件 a.out 长度为 7.2K,可 du 命令报告该文件所使用的磁盘空间总量为 8.0K。很明显,此文件中有很多空洞.

du 命令,参看:du 命令

十、文件截断

#include <unistd.h>
#include <sys/types.h>
int truncate(const char *path, off_t length);
int ftruncate(int fd, off_t length);

1、参数解析

第一个参数:文件路径/文件描述符

第二个参数:文件长度

2、函数功能:修改文件长度

这两个函数将一个现有文件长度截断为 length。如果该文件以前的长度大于 length,则超过 length 以外的数据就不再能访问。如果以前的长度小于 length,文件长度增加,在以前的文件尾端和新的文件尾端之间的数据将读作 0 (也就是可能再文件中创建了一个空洞)。

3、返回值

成功返回 0,失败返回 -1.

4、示例说明

#include <stdio.h>
//示例一  truncate 函数
#include <unistd.h>
#include <stdlib.h>int main (void)
{int res = truncate ("a.txt", 50);if (-1 == res)perror ("fail to truncate"), exit (1);return 0;
}a.txt 为空时:
执行前:
-rw-r--r-- 1 tarena root      0 Mar 31 16:16 a.txt
执行后:
-rw-r--r-- 1 tarena root     50 Apr  5 13:14 a.txt长度截断为50,后面的数据填充为 0。a.txt 不为空时:
执行前:
-rw-r--r-- 1 tarena root    296 Apr  5 13:17 a.txt
执行后:
-rw-r--r-- 1 tarena root     50 Apr  5 13:14 a.txt长度截断为50, 后面的数据不再能访问。
//示例二 ftruncate 函数
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>int main (void)
{int fd = open ("a.txt", O_RDWR);if (-1 == fd)perror ("fail to open"), exit (1);int res = ftruncate (fd, 50);if (-1 == res)perror ("fail to truncate"), exit (1);return 0;
}a.txt 为空时:
执行前:
-rw-r--r-- 1 tarena root      0 Mar 31 16:16 a.txt
执行后:
-rw-r--r-- 1 tarena root     50 Apr  5 13:14 a.txt长度截断为50,后面的数据填充为 0。a.txt 不为空时:
执行前:
-rw-r--r-- 1 tarena root    296 Apr  5 13:17 a.txt
执行后:
-rw-r--r-- 1 tarena root     50 Apr  5 13:14 a.txt长度截断为50, 后面的数据不再能访问。

5、与 O_TRUNC

打开文件时,可以使用 O_TRUNC 来将文件的长度截断为 0.

十一、函数 rename 和 renameat

#include <stdio.h>
int rename(const char *oldname, const char *newname);
int renameat(int oldfd,const char *oldname, int newfd, const char *newname);

1、函数功能

用于文件或目录的重命名

根据 oldname 是指文件、目录还是符号链接,有几种情况需要加以说明。我们必须说明如果 newname 已经存在时将会发生什么
(1)如果 oldname 指的是一个文件而不是目录,那么为该文件或符号链接重命名。在这种情况下,如果 newname 已存在,则它不能引用一个目录。如果 newname 已存在,而且不是一个目录,则先将目录项删除然后将oldname 重命名为 newname。对包含 oldname 的目录以及包含 newname 的目录,调用进程必须具有写权限,因为将更改这两个目录。
(2)如若oldname指的是一个目录,那么为该目录重命名。如果 newname 已存在,则它必须引用一个目录,而且该目录应当是空目录(空目录指只有.和..项)。如果 newname 存在(而且是一个新目录),则先将其删除,然后将oldname 重命名为 newname。另外,当为一个目录重命名时,newname 不能包含 oldname 作为其路径名字的路径前缀。例如,不能将 /usr/foo 重命名为 /usr/foo/testdir,因为旧名字 (/usr/foo) 是新名字的路径前缀,因而不能将其删除。
(3)如若 oldname 或 newname 引用符号链接,则处理的是符号链接本身,而不是它所引用的文件。
(4)不能对 . 和 .. 重命名。
更确切的说,.和..都不能出现在 oldname 和 newname 的最后部分。
(5)作为一个特例,如果 oldname 和 newname 引用同一文件,则函数不做任何更改而成功返回。
如若 newname 已经存在,则调用进程对它需要有写权限。
另外,调用进程将删除 oldname 目录项,并可能要创建 newname 目录项,所以它需要对包含的 oldname 及包含 newname 的目录具有写和执行权限。
除了当 oldname 或 newname 指向相对路径名时,其他情况下 renameat 函数与 rename 函数功能相同。如果 oldname 参数指定了相对路径,就相对于 oldfd 参数引用的目录来计算 oldname。类似的,如果 oldname 参数指定了相对路径,就相对于newfd引用的目录来计算 newname。oldfd 或 newfd 参数都能设置成 AT_FDCWD,此时相对于当前目录来计算相应的路径名。

2、返回值

成功返回0,失败返回 -1.

3、示例说明

#include <stdio.h>
#include <stdlib.h>int main (void)
{rename ("a.txt", "b.txt");return 0;
}

4、Linux 下的 mv 命令

参看:mv 命令

mv命令用来对文件或目录重新命名,或者将文件从一个目录移到另一个目录中。

(1)选项

--backup=<备份模式>:若需覆盖文件,则覆盖前先行备份;
-b:当文件存在时,覆盖前,为其创建一个备份;
-f:若目标文件或目录与现有的文件或目录重复,则直接覆盖现有的文件或目录;
-i:交互式操作,覆盖前先行询问用户,如果源文件与目标文件或目标目录中的文件同名,则询问用户是否覆盖目标文件。用户输入”y”,表示将覆盖目标文件;输入”n”,表示取消对源文件的移动。这样可以避免误将文件覆盖。
--strip-trailing-slashes:删除源文件中的斜杠“/”;
-S<后缀>:为备份文件指定后缀,而不使用默认的后缀;
--target-directory=<目录>:指定源文件要移动到目标目录;
-u:当源文件比目标文件新或者目标文件不存在时,才执行移动操作。

(2)示例

将文件ex3改名为new1
mv ex3 new1

十二、函数 mkdir、mkdirat和 rmdir

用 mkdir 和mkdirat 函数创建目录,用 rmdir 函数删除目录。
#include <sys/stat.h>
int mkdir (const char *pathname, mode_t mode);
int mkdirat(int dirfd, const char *pathname, mode_t mode);

1、函数功能

这两个函数创建一个新的空目录。其中 . 和 .. 目录项是自动创建的。所指定的文件访问权限 mode 由进程的文件模式创建屏蔽字修改。

2、返回值

成功返回0,失败返回 -1。

3、mode 方式

S_IRWXU 00700权限,代表该文件所有者拥有读,写和执行操作的权限
S_IRUSR(S_IREAD) 00400权限,代表该文件所有者拥有可读的权限
S_IWUSR(S_IWRITE) 00200权限,代表该文件所有者拥有可写的权限
S_IXUSR(S_IEXEC) 00100权限,代表该文件所有者拥有执行的权限
S_IRWXG 00070权限,代表该文件用户组拥有读,写和执行操作的权限
S_IRGRP 00040权限,代表该文件用户组拥有可读的权限
S_IWGRP 00020权限,代表该文件用户组拥有可写的权限
S_IXGRP 00010权限,代表该文件用户组拥有执行的权限
S_IRWXO 00007权限,代表其他用户拥有读,写和执行操作的权限
S_IROTH 00004权限,代表其他用户拥有可读的权限
S_IWOTH 00002权限,代表其他用户拥有可写的权限
S_IXOTH 00001权限,代表其他用户拥有执行的权限

4、示例说明

一般,新建目录的权限为 0755
#include <stdio.h>
#include <stdlib.h>int main (void)
{mkdir ("test", 0755);return 0;
}

5、Linux 下的 mkdir 命令

参看:mkdir 命令

(1)选项

-Z:设置安全上下文,当使用SELinux时有效;
-m <目标属性>或--mode<目标属性>建立目录的同时设置目录的权限;
-p或--parents 若所要建立目录的上层目录目前尚未建立,则会一并建立上层目录;
--version 显示版本信息。

(2)示例

在目录/usr/meng下建立子目录test,并且只有文件主有读、写和执行权限,其他人无权访问
mkdir -m 700 /usr/meng/test在当前目录中建立bin和bin下的os_1目录,权限设置为文件主可读、写、执行,同组用户可读和执行,其他用户无权访问
mkdir -p-m 750 bin/os_1

6、函数 rmdir

用 rmdir 函数可以删除一个空目录。空目录是只可包含 . 和 .. 这两项的目录。
#include <unistd.h>
int rmdir( const char *pathname );
返回值:若成功则返回0,若出错则返回-1

如果调用此函数使目录的链接计数成为0,并且也没有其他进程打开此目录,则释放由此目录占用的空间。如果在链接计数达到0时,有一个或几个进程打开了此目录,则在此函数返回前删除最后一个链接及.和..项。另外,在此目录中不能再创建新文件。但是在最后一个进程关闭它之前并不释放此目录。(即使另一个进程打开该目录,它们在此目录下也不能执行其他操作。这样处理的原因是,为了使rmdir函数成功执行,该目录必须是空的。)

(1)示例

#include <stdio.h>
#include <stdlib.h>int main (void)
{rmdir ("test");return 0;
}

7、Linux 下的 rmdir 命令

参看:rmdir 命令  
删除目录必须是 空目录

(1)选项

-p或--parents:删除指定目录后,若该目录的上层目录已变成空目录,则将其一并删除;
--ignore-fail-on-non-empty:此选项使rmdir命令忽略由于删除非空目录时导致的错误信息;
-v或-verboes:显示命令的详细执行过程;
--help:显示命令的帮助信息;
--version:显示命令的版本信息。

(2)示例

删除子目录os_1和其父目录bin
rmdir -p bin/os_1

8、Linux 下的 rm 命令

正如上面的删除目录命令 rmdir 必须是空目录。非空目录则可以使用 rm 命令

(1)选项

-d:直接把欲删除的目录的硬连接数据删除成0,删除该目录;
-f:强制删除文件或目录;
-i:删除已有文件或目录之前先询问用户;
-r或-R:递归处理,将指定目录下的所有文件与子目录一并处理;
--preserve-root:不对根目录进行递归操作;
-v:显示指令的详细执行过程。

(2)示例

删除 test 目录下除隐含文件外的所有文件和子目录
# rm -r test

(3)说明

不过使用 rm 是很危险的,删除的东西是无法恢复。有时养成好的习惯使用 -i 选项删除前先询问用户一下也不错。

十三、读目录

1、opendir 函数

#include <sys/types.h>
#include <dirent.h>
DIR *opendir(const char *name);

(1)函数功能

主要用于打开参数指定的目录,并且返回该目录所在的地址。

(2)返回值

成功返回指针,失败返回NULL

2、readdir 函数

#include <dirent.h>
struct dirent *readdir(DIR *dirp);

(1)函数功能

主要用于读取参数指定的目录中内容,返回参数指针

(2)结构体参数

struct dirent {ino_t          d_ino;       /* inode number */               //i节点的编号off_t          d_off;       /* offset to the next dirent */  //距离下一个子项的偏移量unsigned short d_reclen;    /* length of this record */      //记录的长度unsigned char  d_type;      /* type of file; not supported   //文件的类型by all file system types */ char           d_name[256]; /* filename */                   //文件名};

3、closedir 函数

#include <sys/types.h>
#include <dirent.h>
int closedir(DIR *dirp);

(1)函数功能

主要用于关闭参数指定的目录

(2)示例说明

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <dirent.h>int main (void)
{DIR *dir = opendir ("code");if (NULL == dir)perror ("fail to opendir"), exit (1);struct dirent *ent;while (ent = readdir (dir))printf ("文件名为:%s\n", ent->d_name);closedir (dir);return 0;
}
输出结果:
文件名为:test
文件名为:.
文件名为:..

十四、函数 chdir、fchdir 和 getcwd

1、chdir、fchdir

这两个函数中,分别用 pathname 或打开文件描述符来指定新的当前工作目录
#include <unistd.h>
int chdir(const char *path);
int fchdir(int fd);

(1)函数功能

更改当前工作目录。
工作目录只是进程的一个属性,所以它只影响进程本身。

(2)返回值

成功返回 0,失败返回 -1.

(3)示例说明

//示例一 chdir
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>int main (void)
{if (-1 == chdir ("/tmp"))perror ("fail to chdir"), exit (1);return 0;
}
//示例二 fchdir
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>int main (void)
{int fd = open ("/tmp", O_RDONLY);   if (-1 == fchdir (fd))perror ("fail to fchdir"), exit (1);char buf[100];if (getcwd(buf, 100) == NULL)perror ("fail to getcwd"), exit (1);printf("cwd = %s\n", buf);return 0;
}
输出结果:
cwd = /tmp

(4)示例总结

执行示例,使用 pwd 命令查看当前目录可以发现,并没有发生改变。这是因为每个程序运行在独立的进程中,shell 的当前工作目录并不会随着程序调用 chdir 而改变。由此可见,为了改变 shell 进程自己的工作目录,shell 应当直接调用 chdir 函数,为此, cd 命令内建在 shell 中。

2、函数 getcwd

#include <unistd.h>
char *getcwd(char *buf, size_t size);

(1)函数功能

获取当前工作目录的绝对路径

(2)返回值

成功返回 buf,失败返回 NULL

(3)示例说明

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>int main (void)
{if (chdir("/usr/bin/") < 0)perror ("chdir failed"), exit (1);char buf[100];if (getcwd(buf, 100) == NULL)perror ("fail to getcwd"), exit (1);printf("cwd = %s\n", buf);return0;
}
输出结果:
cwd = /usr/bin

(4)Linux 下的 pwd 命令

pwd 命令以绝对路径的方式显示用户当前工作目录。
命令将当前目录的全路径名称(从根目录)写入标准输出。全部目录使用 / 分隔。第一个 / 表示根目录,最后一个目录是当前目录。执行 pwd 命令可立刻得知您目前所在的工作目录的绝对路径名称。

十五、未讲部分:

设置用户 ID和设置组 ID
新文件和目录的所有权
粘着位
文件系统
函数 link
符号链接
创建和读取符号链接
文件的时间
函数 futimens
设备特殊文件

UNIX再学习 -- 文件和目录相关推荐

  1. UNIX再学习 -- 文件I/O

    上一篇讲完文件描述符,接下来进入正题,文件的处理函数. 一.函数 open 详细内容,可自行 man creat 查看 #include <sys/types.h> #include &l ...

  2. UNIX再学习 -- 文件描述符

    在 UNIX/Linux 系统中,一切皆文件,这句话想必都有听过.对于文件的操作几乎适用于所有的设备,这也就看出了文件操作的重要性了.在C语言再学习部分有讲过标准I/O文件操作,参看:C语言再学习 - ...

  3. UNIX再学习 -- 守护进程(转)

    参看:守护进程 一.什么是守护进程 守护进程(Daemon Process),也就是通常说的 Daemon 进程(精灵进程),是 Linux 中的后台服务进程.它是一个生存期较长的进程,通常独立于控制 ...

  4. UNIX再学习 -- 用户 ID 和组 ID

    用户 ID和组 ID 的内容已经在好几章中出现过了.之前都没有讲到,现在放到一起总结. 一.用户 ID 和 组 ID 回顾 1.我们在APUE 第 4.6.8 章,都有涉及到. 其中我们用到的地方: ...

  5. UNIX再学习 -- 信号

    终于讲到信号部分,很多比较重要的应用程序都需处理信号.第 9 章需要先了解信号机制再看,所以先跳过不讲.现在开始详解信号. 一.信号概念 信号是提供异步事件处理机制的软件中断. 这些异步事件可能来自硬 ...

  6. UNIX再学习 -- exec 函数族

    我们在讲,文件I/O的时候,简单提到过 exec 函数,讲到 vfork 的时候,也有用到.下面我们来详细介绍下它. 参看:UNIX再学习 -- 文件I/O  参看:UNIX再学习 -- 函数 for ...

  7. UNIX再学习 -- 记录锁

    APUE第 3 章,参看:UNIX再学习 -- 文件I/O  fcntl 函数它的记录锁功能我们当时没讲.接下来就详细说明下. 一.读写冲突 1.如果两个或两个以上的进程同时向一个文件的某个特定的区域 ...

  8. UNIX再学习 -- 标准I/O

    这部分之前有所总结: 参看:C语言再学习 -- 文件 参看:C语言再学习 -- 输入/输出 参看:UNIX再学习 -- 文件描述符 对比:UNIX再学习 -- 文件I/O 一.流 文件I/O中,所有的 ...

  9. UNIX再学习 -- 进程间通信之管道

    一.进程间通信概念 首先,需要了解一下什么是进程间通信. 进程之间的相互通信的技术,称为进程间通信(InterProcess Communication,IPC). 下图列出 4 种实现所支持的不同形 ...

最新文章

  1. 微信公众平台开发(45)食物营养及热量查询
  2. mysql存储过程迭代自己获取所有子孙
  3. 锐捷校园网环境下使用虚拟机上网
  4. RN开发关闭所有黄色警告弹出(console.warn())
  5. TCP和UDP区别?如何改进TCP
  6. 2022年最新前端面试题(大前端时代来临卷起来吧小伙子们..持续维护走到哪记到哪)
  7. avx2指令集对php有用吗,AVX2指令集的作用
  8. Ajax 请求的http头信息特点 x-requested-with
  9. turtle库使用——漫天繁星+万花筒绘制
  10. 异步协议与同步协议:面向字符的协议BSC协议
  11. 格力(GREE)家用移动空调免安装一体机空调KY-23NK 清灰拆装教程
  12. 发生系统错误 5。拒绝访问。
  13. 计算机系班级未来展望,大学生班长对班级未来的展望.doc
  14. 不换门可以改开门的方向吗_不换门可以改开门的方向吗?
  15. Android FOTA 升级流程
  16. 最新版泛域名证书申请
  17. 32.构造ICMP请求包进行路由跟踪
  18. 中山一院——新一代的智慧医院建设,以流量分析为抓手,提升用户体验
  19. ios沙箱软件_iOS应用软件沙盒sandbox相关知识(整理)
  20. dpdk-18.11网卡多队列RSS设置

热门文章

  1. Android:sqlite问题小结
  2. 截取两个标签之间的文本
  3. web项目中遇到的Maven包依赖冲突问题解决
  4. 查看Linux下网卡状态或 是否连接(转)
  5. ((ios开发学习笔记 十))代码实现自定义TableView
  6. 关于CheckBoxList和RadioButtonList的几个问题
  7. 编程方法学23:搜索排序与算法效率分析
  8. Python学习笔记:基础
  9. 二维指针*(void **)的研究(uC/OS-II案例)
  10. 高斯平稳随机过程仿真