版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
文件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 命令:可以用来查看一个目录的内容
路径可以用来表示文件或者文件夹所在的位置,路径是从一个文件夹开始走到另一个文件夹或者文件位置中间的这条路。把这条路经过的所有文件夹名称按顺序书写出来的结果就可以表示这条路。
路径分为绝对路径和相对路径
绝对路径:起点必须是根目录,如 /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 fields
-
S_IFSOCK
0140000 socket
-
S_IFLNK
0120000 symbolic link
-
S_IFREG
0100000 regular file
-
S_IFBLK
0060000 block device
-
S_IFDIR
0040000 directory
-
S_IFCHR
0020000 character device
-
S_IFIFO
0010000 FIFO
-
S_ISUID
0004000
set UID bit
-
S_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 permission
-
S_IWUSR 00200 owner has write permission
-
S_IXUSR 00100 owner has execute permission
-
-
S_IRWXG 00070 mask for group permissions
-
S_IRGRP 00040 group has read permission
-
S_IWGRP 00020 group has write permission
-
S_IXGRP 00010 group has execute permission
-
-
S_IRWXO 00007 mask for permissions for others (not in group)
-
S_IROTH 00004 others have read permission
-
S_IWOTH 00002 others have write permission
-
S_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);
-
//设置新的屏蔽字,返回旧的系统默认
-
// 当前系统默认屏蔽 0055
-
printf (
"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
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
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 命令
(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 不变。
如若两个参数 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)如若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 命令
(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 命令
-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
设备特殊文件