UNIX再学习 -- 文件和目录

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

一、函数 stat、fstat、fstatat 和 lstat

详细内容可 man fstat 查看

#include 
#include 
#include 

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 
#include 
#include 
#include 
#include 
#include 
#include 

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 
#include 
#include 
#include 
#include 

int main(int argc, char *argv[])
{
	struct stat sb;

	if (argc != 2) {
		fprintf(stderr, "Usage: %s \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 
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 
#include 

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 
#include 
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 
#include 
#include 
#include 
#include 
#include 

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 
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 
#include 
#include 
#include 
#include 
#include 

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 
#include 
#include 
#include 
#include 
#include 

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 
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   
#include   
  
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 
#include 
#include 
#include 
#include 
#include 

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 
#include 
#include 

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,文件空洞不占用磁盘空间,但被计算在文件大小之内。
UNIX再学习 -- 文件和目录_第1张图片

作为一个例子,考虑下列情况:
# 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 
#include 
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 
//示例一  truncate 函数
#include 
#include 

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 
#include 
#include 
#include 
#include 
#include 

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   
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 
#include 

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 
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 
#include 

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

(1)示例

#include 
#include 

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 
#include 
DIR *opendir(const char *name);

(1)函数功能

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

(2)返回值

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

2、readdir 函数

#include 
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 
#include 
int closedir(DIR *dirp);

(1)函数功能

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

(2)示例说明

#include 
#include 
#include 
#include 

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 
int chdir(const char *path);
int fchdir(int fd);

(1)函数功能

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

(2)返回值

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

(3)示例说明

//示例一 chdir
#include 
#include 
#include 

int main (void)
{
	if (-1 == chdir ("/tmp"))
		perror ("fail to chdir"), exit (1);

	return 0;
}
//示例二 fchdir
#include 
#include 
#include 
#include 
#include 
#include 


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 
char *getcwd(char *buf, size_t size);

(1)函数功能

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

(2)返回值

成功返回 buf,失败返回 NULL

(3)示例说明

#include 
#include 
#include 

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再学习,UNIX再学习)