Linux文件操作

一、系统api与库函数的关系

linux系统默认启动程序时会打开stdin/stdout/stderr ,我们可以直接使用通过<unistd.h>库中的close方法可以指定关闭上面的标准输入/输出和错误, 例如close(1)表示关闭标准输出.

通过open命令可以打开一个输出,此时如果标准输出关闭了,那么open命令可以替换标准输出.
通过<stdio.h>库中的fflush方法可以刷新标准输入和输出的缓存区buffer,例如 fflush(stdout);

二、open和close函数介绍

通过man 2 open/close可以查看open/close函数的用法,或者在编写代码的时候,在命令模式下光标移动到函数名上,按下2(表示第2章) 再按下K 就可以跳到这个函数的介绍了.

2.1 open方法介绍

在这里插入图片描述
open方法表示打开一个文件描述符,其中pathname是文件名

flags的必选项(必选项只能选其中一个):

  • O_RDONLY :read-only
  • O_WRONLY:write-only
  • O_RDWR:read/write

flags的可选项有:

  • O_APPEND(追加)
  • O_CREAT(若文件不存在则创建)
  • O_EXCL(必须和O_CREAT一起使用,若文件存在的报错)
  • O_NONBLOCK(非阻塞)

mode:表示权限位,最终是(mode & ~umask)

可选项是可以和必选项结合使用的

返回值:是返回最小的可用的的文件描述符,假设存在stdin/stdout/stderr都存在,那么open返回最小是3.因为0,1,2被占用了.失败时返回-1.

案例-通过open简单实现一个touch命令的功能

#include<stdio.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/types.h>

int main(int argc,char* args[])
{
	if(argc !=2)
	{
		printf("./touch filename\n");
		return -1;
	}
	int fd = open(args[1],O_RDONLY|O_CREAT,0666);// 0666是mode,后面会介绍
	close(fd);
	return 0;
}

在这里插入图片描述

2.2 close方法介绍

在这里插入图片描述
close表示关闭一个文件描述符.返回值,成功返回0,失败返回-1.

三、open/create函数创建文件时的权限设置

首先了解一下 umask 命令,该命令用来设置限制新文件权限的掩码。当新文件被创建时,其最初的权限由文件创建掩码决定。简单地来说,umask和open()及creat()函数的权限码(mode_t mode参数)共同决定你的新建文件的权限。具体关系为mode & ~umask。

下面通过简单的程序来验证它们之间的关系。
由于open()和creat()创建文件,结果一致,我们直接采用creat()函数:

#include<stdio.h>
#include<fcntl.h>
#include<sys/stat.h>

int main()
{
	if(creat("1.log",0777)<0)
	{
		printf("创建文件失败!\n");
	}else
	{
		printf("创建文件成功!\n");
	}
	return 0;
}

编译执行后,查看生成文件1.log的权限
在这里插入图片描述
权限是775,这和我们的期望777不符,为什么呢? 这是因为creat和open创建文件时的权限是 mode & ~umask的结果, mode虽然是0777,但是普通用户的umask是0002,如下所示:
在这里插入图片描述
所以~umask (umask取反,也就是0002取反)的结果是7775, 套公式mode & ~umask 也就是0777 & 7775 的结果是0775, 也就是1.log文件的最终权限是rwx rwx r-x

那么如何避免umask的影响呢?
我们可以使用umask函数,将值设置为0000,那么~umask的值就变成7777了, 由于7的二进制是111, 不会影响&操作的结果. 将上面的代码修改如下:

#include<stdio.h>
#include<fcntl.h>
#include<sys/stat.h>

int main()
{
	umask(0000);//将umask的值临时修改为0000,这个不会修改系统的umask默认值.

	if(creat("1.log",0777)<0) //临时修改了umask值后,设置的最终权限结果就是0777
	{
		printf("创建文件失败!\n");
	}else
	{
		printf("创建文件成功!\n");
	}
	return 0;
}

重新编译执行,查看1.log的权限就变成了777了
在这里插入图片描述
当然如果只是创建普通文件的话,没必要添加执行权限,一般就是666权限就可以了.也就是rw-rw-rw-

umask的值表示的权限是u/g/o所"不具备"的权限,它是Linux的默认权限,root用户是0022,表示root用户创建文件或者文件夹时,g(所属组)和o(其他组)将不具备w权限,普通用户是0002,表示普通用户创建目录或者文件时,o(其他组)将不具备w权限.
所以,有了umask的限制之后,我们通过库函数设置的权限就可以符合Linux下使用命令创建文件的权限效果一样.例如普通用户在Linux上使用touch命令创建的文件的权限就是664, 那么我们使用库函数创建文件就可以将mode设置为0666,这样0666与7775的结果就是0664 ,6和5的二进制&的结果就是4.
当然,如果你觉得计算太麻烦, 那么我们其实根据umask规则也可以知道,普通用户的o是没有w权限,所以mode设置为0666的最后结果就是0664 (0666-0002)
除非你需要明确添加w权限,那么你可以通过umask函数临时去掉限制.

四、read和write函数的介绍和使用

4.1 read函数介绍

read函数需要导入这些头文件

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

函数原型如下
在这里插入图片描述
fd:文件描述符
buf:缓冲区
count:缓冲区大小
ssize_t:返回值,返回读取的实际字节长度. -1表示失败, 0 表示读到文件末尾

4.2 write函数介绍

在这里插入图片描述
fd:文件描述符
buf:缓冲区
count:缓冲区大小,注意字符串不需要考虑’\0’所占的1个字节,例如"abc"字符串,对应的count就是3
ssize_t:返回值,成功返回写入的实际字节数, -1表示失败, 0表示未写入.

注意:write完后,指针的标记是停留在最后一个字节的,此时如果直接用read来读是没法读取内容的.

4.3 如何使用

例如实现一个cat命令的功能

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

int main(int argc,char* args[])
{
	if(argc !=2)
	{
		printf("usage: ./cat filename");
		return -1;
	}
	int fd = open(args[1],O_RDONLY);
	
	char buf[1024] = {0};
	int len = -1;
	while((len = read(fd,buf,sizeof(buf)))!=0)
	{
		//将读取的内容输出到屏幕
		write(STDOUT_FILENO,buf,len); //其中STDOUT_FILENO是stdout的宏定义,值是1,表示标准输出
	}

	close(fd);

	return 0;
}

编译后,执行效果如下:
在这里插入图片描述

五、lseek函数的介绍和使用

5.1 介绍

作用类似fseek函数,用于设置当前文件读或者写的位置,参数介绍如下:
在这里插入图片描述
fd:文件描述符
offset:偏移量,通常是0,结合whence来使用,用来定位到文件的开头,文件的当前和文件的结束位置.
whence: 表示从什么位置开始设置,有3个值,

  • SEEK_SET(文件开头位置)
  • SEEK_CUR(文件当前读写的位置)
  • SEEK_END(文件结束位置)

off_t :成功返回当前位置到开始位置的长度(字节数),失败返回-1.

5.2 如何使用

如果要实现open() … write()… read()…close() 方式来操作同一个文件的话,那么write()和read()之间就需要用到lseek()了.因为write()完后,标记位置的指针已经指向文件末尾了,此时需要用lseek()重置到开头位置,然后read()才能读到内容.

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

int main(int argc, char* args[])
{
	int fd = open("./test2.txt",O_RDWR|O_CREAT,0666); //打开./test2.txt,若不存在则创建
	//先写,输出11个字节,包括了\n
	write(fd,"helloworld\n",11);
	
	//重置到开始位置
	lseek(fd,0,SEEK_SET);

	//后读
	char buf[1024] = {0};
	int len = read(fd,buf,sizeof(buf));
	if(len)
	{	
		//显示到屏幕 fd=STDOUT_FILENO
		write(STDOUT_FILENO,buf,len);
	}
     //最后关闭
	close(fd);

	return 0;
}

在这里插入图片描述

另外使用lseek还能用来获取文件的大小, 因为lseek的返回值就是从文件开头到lseek定位的位置之间的长度(字节数), 假设直接调用lseek移动到文件末尾,那么返回的就是文件的大小了.
例如:

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

int main(int argc,char* args[])
{
	if(argc != 2)
	{
		printf("Usage: ./len filename");
		return -1;
	}

	int fd = open(args[1],O_RDONLY);
	int len = lseek(fd,0,SEEK_END);
	printf("file len is: %d\n",len);
	close(fd);
}

编译执行,查看文件的大小,刚好和ll命令查看的一样
在这里插入图片描述

六、阻塞和非阻塞相关概念

当使用read函数读设备或者管道或者网络的时候就会出现阻塞现象.
下面通过输入输出设备来模拟, 对应的位置是/dev/tty

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

int main()
{
	char buf[1024] = {0};
	int len = 0;
	int fd = open("/dev/tty",O_RDWR); // 设置读写来源于设备的输入输出
	while(1)
	{
		int len = read(fd,buf,sizeof buf); //由于读取的是设备,所以read会阻塞在此,直到用户输入内容
		if(len)
		{
			printf("buf is : %s\n",buf);
		}

		printf("haha\n");//这句话虽然在while true里面,但是由于read阻塞了,所以并不会一直输出
	}
	close(fd);
	return 0;
}

从结果可以看出,输入内容后,read才会走下一步,然后再次循环又停在了read函数处等等用户继续输入,这种现象就是阻塞
在这里插入图片描述
如果不想被阻塞该怎么处理?
只需要将read的flag新增一个O_NONBLOCK即可,如下所示:

int fd = open("/dev/tty",O_RDWR|O_NONBLOCK);

或者使用<fcntl.h>中的fcntl函数.

6.1 fcnl函数

在这里插入图片描述
cmd参数可选有:

  • F_GETFL ,如果用这个那么可以省略第3个参数
  • F_SETFL ,如果用这个那么第三个参数必须是int类型

用法如下:

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

int main()
{
	char buf[1024] ={0};
	int len = 0;
	int fd = open("/dev/tty",O_RDONLY);

	//1.先获取标记
	int flags = fcntl(fd,F_GETFL);
	//2.添加非阻塞标记
	flags |= O_NONBLOCK;
	//3.重新设置标记
	fcntl(fd,F_SETFL,flags);

	while(1)
	{
		int len = read(fd,buf,sizeof buf);
		if(len)
		{
			printf("buf is:%s\n",buf);
		}
		printf("haha\n");
	}

	close(fd);
	return 0;
}

七、Linux最大文件打开数

在Linux下有时会遇到Socket/File : Can’t open so many files的问题。其实Linux是有文件句柄限制的,而且Linux默认一般都是1024(阿里云主机默认是65535)。在生产环境中很容易到达这个值,因此这里就会成为系统的瓶颈。

使用ulimit -a 或者 ulimit -n
在这里插入图片描述
open files (-n) 1024 是linux操作系统对一个进程打开的文件句柄(文件描述符表)数量的限制(也包含打开的套接字数量。

7.1 修改单个进程的最大文件句柄

通过下面的命令可以

ulimit -SHn 10000

其实ulimit 命令身是分软限制和硬限制,加-H就是硬限制,加-S就是软限制。默认显示的是软限制,如果运行ulimit 命令修改时没有加上-H或-S,就是两个参数一起改变。

软限制和硬限制的区别?

硬限制就是实际的限制,而软限制是警告限制,它只会给出警告。

要想ulimits 的数值永久生效,必须修改配置文件/etc/security/limits.conf
在该配置文件中添加

* soft nofile 65535   
* hard nofile 65535  

echo "* soft nofile 65535"  >> /etc/security/limits.conf

echo "* hard nofile 65535"  >> /etc/security/limits.conf

其中 * 表示所用的用户

7.2 修改系统所有进程的文件局柄

上面的修改只是对一个进程打开的文件句柄数量的限制,我们还需要设置系统的总限制才可以。

假如,我们设置进程打开的文件句柄数是1024 ,但是系统总线制才500,所以所有进程最多能打开文件句柄数量500。从这里我们可以看出只设置进程的打开文件句柄的数量是不行的。所以需要修改系统的总限制才可以。

echo 6553560 > /proc/sys/fs/file-max

上面是临时生效方法,重启机器后会失效;

永久生效方法:

修改 /etc/sysctl.conf, 加入

fs.file-max = 6553560重启生效

通过下面代码可以验证这个文件句柄的限制

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

int main()
{
	int num = 0;
	char filename[128] = {0};
	while(1)
	{
		sprintf(filename,"temp_%04d",num++); //%04表示不够4位用0补齐.
		if(open(filename,O_RDONLY|O_CREAT,0666)<0)
		{
			perror("open error");
			break;
		}
	}
	return 0;
}

如下图所示一共生成了temp_0000~temp_1020个文件,也就是1021个,为啥不是1024呢,因为文件描述符表中0 ,1 ,2 默认打开程序的时候就被stdin/stdout/stderr占用了. 所以加上这3个刚好就是1024个.
在这里插入图片描述

八、stat函数介绍和使用

在这里插入图片描述
这个函数用于获取文件的信息,主要指文件的属性,在C基础(七)文件操作也有介绍过

参数:

  • pathname:文件名
  • statbuf:传出参数,需要外部定义一个struct stat类型的变量,然后传入地址.

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

stat结构体的成员信息如下:
在这里插入图片描述
如何使用?

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

int main(int argc,char* args[])
{
	if(argc !=2)
	{
		printf("Usage: ./stat filename\n");
		return -1;
	}

	struct stat st; //定义一个结构体
	
	//调用stat函数,参数1是文件名,参数2是上面定义的结构体的地址,函数内部会将获取的信息封装到st变量中
	stat(args[1],&st); 

	return 0;
}

编译源文件

gcc -o stat statUsage.c -g

然后通过gdb调用启动程序查看statUsage.c文件的stat的内容.
在这里插入图片描述
对应的内容就是stat命令中的内容:
在这里插入图片描述

8.1 通过stat函数实现ll命令的功能

首先需要介绍如何获取用户名,通过getpwuid函数实现
在这里插入图片描述
需要传递uid参数,用户id,返回的是一个passwd的结构体指针
在这里插入图片描述
然后是获取组信息,通过getgrgid函数来实现
在这里插入图片描述
需要传递gid参数,组id,返回的是一个group的结构体指针
在这里插入图片描述
然后通过localtime函数可以查看本地时间
在这里插入图片描述
返回的是tm结构体
在这里插入图片描述
接下来就是要实现下图效果的编码了
在这里插入图片描述

#include<stdio.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<unistd.h>
#include<fcntl.h>
#include<string.h>
#include<time.h>
#include<pwd.h>
#include<grp.h>

int main(int argc ,char * args[])
{

	if(argc !=2)
	{
		printf("Usage: ./ll filename");
	}

	struct stat st;
	stat(args[1],&st);

	char stmode[11] = {0};
	memset(stmode,'-',10);

	//文件类型
	if(S_ISREG(st.st_mode)) stmode[0]='-';//普通文件
	if(S_ISDIR(st.st_mode)) stmode[0]='d';//文件夹
	if(S_ISCHR(st.st_mode)) stmode[0]='c';//字符设备文件
	if(S_ISBLK(st.st_mode)) stmode[0]='b';//块设备文件
	if(S_ISFIFO(st.st_mode))stmode[0]='p';//管道文件
	if(S_ISLNK(st.st_mode))stmode[0]='l';//软连接文件
	if(S_ISSOCK(st.st_mode))stmode[0]='s';//socket文件

	//权限描述 user
	if(st.st_mode & S_IRUSR) stmode[1] = 'r';
	if(st.st_mode & S_IWUSR) stmode[2] = 'w';
	if(st.st_mode & S_IXUSR) stmode[3] = 'x';
	//权限描述符 group
	if(st.st_mode & S_IRGRP) stmode[4] = 'r';
	if(st.st_mode & S_IWGRP) stmode[5] = 'w';
	if(st.st_mode & S_IXGRP) stmode[6] = 'x';
	//权限描述符 other
	if(st.st_mode & S_IROTH) stmode[7] = 'r';
	if(st.st_mode & S_IWOTH) stmode[8] = 'w';
	if(st.st_mode & S_IXOTH) stmode[9] = 'x';
	
	//通过localtime函数获取时间
	struct tm *filetm = localtime(&st.st_atim.tv_sec);

	char timebuf[20]={0};
	sprintf(timebuf,"%d月   %d %02d:%02d",filetm->tm_mon+1,filetm->tm_mday,
			filetm->tm_hour,filetm->tm_min);
			
	//通过getpwuid getgrgid函数获取用户id和组id
	printf("%s %ld %s %s %ld %s %s\n",stmode,st.st_nlink,getpwuid(st.st_uid)->pw_name,
			getgrgid(st.st_gid)->gr_name,st.st_size,timebuf,args[1]);
	
	return 0;
}

编译执行,效果如下,和ll命令完全一样.
在这里插入图片描述

8.2 stat与lstat的区别

stat遇到软连接会穿透查看源文件的信息,包括文件真实大小,而lstat则不会,查看软连接时显示的是软连接的大小.软连接的大小通常是固定的. 系统的ll命令的效果就是没有穿透的.例如:
在这里插入图片描述
ll命令查看t1.c软连接显示是7个字节, 而用我上面写的Myll.c程序查看的是335,因为Myll.c用的是stat实现的.

九、access和truncate函数使用

9.1 access函数

用于判断当前用户是否具有读/写/执行的权限,以及判断文件是否存在

mode参数有:

  • R_OK :判断是否可读
  • W_OK:判断是否可写
  • X_OK:判断是否可执行
  • F_OK:判断文件是否存在

返回值:
文件存在或者存在某权限返回0 ,否则返回-1

#include<stdio.h>
#include<unistd.h>

int main(int argc,char* args[])
{
	if(argc != 2)
	{
		printf("Usage: ./access filename");
		return -1;
	}

	if(access(args[1],R_OK) ==0)
		printf("%s read ok\n",args[1]);

	if(access(args[1],W_OK)==0)
		printf("%s write ok\n",args[1]);

	if(access(args[1],X_OK)==0)
		printf("%s exe ok\n",args[1]);

	if(access(args[1],F_OK)==0)
		printf("%s file exist\n",args[1]);


	return 0;
}

编译执行结果如下:
在这里插入图片描述

9.2 truncate函数

用于截断文件
在这里插入图片描述
参数

  • path: 文件名 length:
  • 长度,如果长度大于原文件那么会扩展,用其他符号填充,如果小于原文件,则截断为length长度.

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

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<stdlib.h>

int main(int argc,char* args[])
{
	if(argc !=3)
	{
		printf("Usage: truncate filename length\n");
		return -1;
	}
	truncate(args[1],atoi(args[2]));

	return 0;
}

编译执行,可以看到hello文件的大小变成了1024字节.
在这里插入图片描述

十、链接函数的使用

10.1 link(硬链接)

制造硬链接
在这里插入图片描述
参数:

  • oldpath:原文件
  • newpath:硬链接文件

返回值
0:成功 -1:失败

10.2 symlink(软/符号链接)

制造软链接
在这里插入图片描述
参数:

  • target:原文件
  • linkpath:软连接文件

返回值
0:成功 -1:失败

10.3 readlink

用于读取软(符号)链接本身的内容,得到该链接指向的文件名
在这里插入图片描述
参数

  • pathname:软链接文件名
  • buf:缓冲区
  • bufsiz:缓冲区大小

返回值
成功返回读到的软连接的长度,失败返回-1.

10.4 unlink

删除软(符号)链接或者硬链接或者文件, 注意:unlink在删除文件的时候如果当前程序正在访问该文件,那么unlink不会立马删除该文件,而是等等程序退出的时候才去删除.
在这里插入图片描述
参数pathname: 对应的链接名字 或者原文件名

返回值
0:成功 -1:失败

#include<stdio.h>
#include<unistd.h>

int main()
{
	//创建硬链接
	link("hello","hello.hard");

	//创建软连接
	symlink("hello","hello.soft");

	//读取软连接指向的原文件名称
	char buf[64]={0};
	readlink("hello.soft",buf,sizeof buf);
	printf("hello.soft链接指向的原文件名是:%s\n",buf);

	return 0;
}

编译执行
在这里插入图片描述
删除软硬链接和原文件

//删除硬链接
unlink("hello.hard");
//删除软链接
unlink("hello.soft");
//删除原文件
unlink("hello");

删除后再次运行上面程序输出结果如下:
在这里插入图片描述

十一、chown和rename函数的使用

11.1 chown

改变用户和组
在这里插入图片描述
参数

  • pathname:文件名
  • owner:用户id,参考/etc/passwd
  • group:组id ,参考/etc/group

11.2 rename

重命名文件
在这里插入图片描述
参数

  • oldpath:旧文件(目录)
  • newpath:新文件(目录)

返回值
成功:0 失败:-1

十二、 chdir和getcwd函数的使用

12.1 chdir

改变进程工作目录
在这里插入图片描述
参数path:对应的目标路径

返回值
成功: 0 失败-1

12.2 getcwd

获取当前进程的工作路径
在这里插入图片描述
参数

  • buf:传出参数,路径
  • size_t: buf的大小

返回值
成功返回路径的指针,失败返回NULL

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>

int main()
{
	//切换当前进程的工作目录
	chdir("bbb");
	
	int fd = open("tmp",O_WRONLY|O_CREAT,0666);
	char* str = "到此一游\n";
	write(fd,str,strlen(str));

	//显示当前进程工作目录
	char buf[64] = {0};
	getcwd(buf,sizeof buf);

	printf("buf is [%s]\n",buf);

	return 0;
}

编译执行
在这里插入图片描述

十三、mkdir函数的使用

创建目录
在这里插入图片描述
参数

  • pathname:目录名
  • mode:权限模式,最终结果是mode & ~umask &0777, 如果目录没有可执行权限是无法进入的,所以使用这个函数创建目录时,mode通常是0777, 也就是普通用户执行后,最终权限是0775( 0777-0002)

十四、rmdir/opendir/readdir/closedir函数的使用

14.1 rmdir

删除空目录
在这里插入图片描述

14.2 opendir

打开目录
在这里插入图片描述

14.3 readdir

读取目录,参数是opendir的返回值
在这里插入图片描述
返回值是struct dirent结构体指针,返回NULL表示读到结尾
在这里插入图片描述
其中d_type=4表示目录,d_type=8(对应的宏定义是DT_REG)表示普通文件, 文件类型声明如下:

/*
 * File types
 */
#define DT_UNKNOWN       0
#define DT_FIFO          1
#define DT_CHR           2
#define DT_DIR           4 // 目录文件
#define DT_BLK           6
#define DT_REG           8 // 普通文件
#define DT_LNK          10
#define DT_SOCK         12
#define DT_WHT          14

14.4 closedir

关闭目录,参数也是opendir的返回值
在这里插入图片描述

14.5 案例-递归统计子目录中的普通文件个数

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <dirent.h>
#include <string.h>

// 定义全局变量
int count = 0;

// 计算文件数量
int DirCount(char *path)
{
	printf("%s\n", path);
	//打开目录
	DIR *dirp = opendir(path);
	if (dirp == NULL)
	{
		perror("opendir err");
		return -1;
	}
	//打开目录成功,读取目录
	struct dirent *direntp = NULL;
	while ((direntp = readdir(dirp)) != NULL)
	{
		if (direntp->d_type == DT_DIR)
		{
			//如果是目录,排除. 和..目录
			if (strcmp(".", direntp->d_name) == 0 ||
				strcmp("..", direntp->d_name) == 0)
			{
				continue;
			}
			//递归进入子目录
			char newdirname[1024] = {0};
			sprintf(newdirname, "%s/%s", path, direntp->d_name);
			DirCount(newdirname);
		}
		//统计普通文件个数
		if (direntp->d_type == DT_REG)
		{
			count++;
			printf("dname:%s\n", direntp->d_name);
		}
	}
	//关闭目录
	closedir(dirp);
	return 0;
}

int main(int argc, char *args[])
{
	if (argc != 2)
	{
		printf("Usage: ./coutdir dirname");
	}

	DirCount(args[1]);

	printf("count=%d\n", count);
}

编译执行
在这里插入图片描述

十五、errno说明

记录的一些错误码和相关描述,在下面的文件中可以查看

 /usr/include/asm-generic/errno-base.h
 /usr/include/asm-generic/errno.h

当然你也可以在代码中通过strerror函数传入errno来获取错误描述
在这里插入图片描述

十六、dup/dup2函数的使用

在这里插入图片描述

  • dup
    用于复制文件描述符,可以用于备份
    返回值
    成功:返回新的文件描述符指向oldfd对应的文件
    失败:返回-1,设置errno

  • dup2
    重定向,让新的文件描述符指向旧的文件描述
    返回值
    成功:关闭newfd对应的文件描述符,将newfd重新指向oldfd对应的文件
    失败:返回-1,设置errno

  • 5
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值