在学习Linux命令基础总结了笔记,并分享出来。
08-linux-day04(stat-readdir-dup2)
目录:一、学习目标二、文件和目录操作1、打开最大文件数量2、stat函数介绍3、stat函数介绍2与stat命令4、实现ls -l命令5、stat与lstat的区别6、access与truncate7、readlink、unlink8、unlink补充9、chown与rename10、chdir、getcwd切换目录和获得工作路径11、mkdir创建目录12、读目录相关函数介绍13、递归子目录统计普通文件个数14、errno说明15、dup2和dup说明16、dup2和dup的使用
一、学习目标
1、掌握stat/lstat函数的使用
2、了解文件属性相关的函数使用
3、了解目录操作相关的函数的使用
4、掌握目录遍历相关函数的使用
5、掌握dup、dup2函数的使用
6、掌握fcntl函数的使用
虚函数地址空间
二、文件和目录操作
1、打开最大文件数量
### xxx.c ---> xxx
>touch makefile
>vi makefile
1 ### xxx.c ---> xxx
2 SrcFiles=$(wildcard *.c)
3 TargetFiles=$(patsubst %.c,%,$(SrcFiles))
4
5 all:$(TargetFiles)
6
7 %:%.c
8 gcc -o $@ $^ -g
9
10 clean:
11 rm -f $(TargetFiles)
>touch openmax.c
>vi openmax.c
1 #include<stdio.h>
2 #include<sys/types.h>
3 #include<sys/stat.h>
4 #include<fcntl.h>
5 #include<unistd.h>
6
7 int main()
8 {
9 int num = 3;
10 char filename[128]={0};
11 while(1)
12 {
13 sprintf(filename,"temp_%04d",num++);
14 if(open(filename,O_RDONLY|O_CREAT,0666) < 0)
15 {
16 perror("open error");
17 break;
18 }
19 }
20 printf("num == %d\n", num);
21 return 0;
22 }
>make
>./openmax
open err:Too many open files
num == 1025
2、stat函数介绍
》man 2 stat
int stat(const char* pathname, struct stat *buf);
存储原理:
>touch hello
>vi hello
(随便输入内容后保存退出)
>ln hello hello.hard
>ls -lrt
>ls -i hello*
输出:2642317 hello 2642317 hello.hard(ls -i 指定文件——查看指定文件的索引号)
查找timespec结构体
>sudo grep -rn "struct timespec {" /usr/
查到time.h的位置 打开
>vi +9 文件位置
struct timespec {
__kernel_time_t tv_sec;/*seconds*/当前时间到1970.1.1 0:0:0秒数
long tv_nsec;/*nanoseconds*/纳秒
};
3、stat函数介绍2与stat命令
》stat函数参数
pathname 文件名
struct stat *buf 传出参数,定义struct stat sb;&sb
返回值:成功返回0,失败返回-1
>touch stat.c
>vi stat.c
1 #include<stdio.h>
2 #include<sys/types.h>
3 #include<sys/stat.h>
4 #include<unistd.h>
5
6 int main(int argc, char* argv[])
7 {
8 if(argc != 2)
9 {
10 printf("./a.out filename\n");
11 return -1;
12 }
13 struct stat sb;
14 stat(argv[1],&sb);
15 return 0;
16 }
保存退出
>make
>gdb stat
(gdb)b main
(gdb)run hello
(gdb)n
回车直到return 0;
(gdb)p sb
输出了结构体sb的信息
(gdb)p/o sb.st_mode
p/o 变量名:可以8进制查看某变量
》stat命令
>stat stat.c
还可以输出文件最近访问的时间(文件最新被读的时间)、最近更改的时间(内容更改)、最近改动的时间(大小、权限、硬链接等属性更改)
扩展:重定向可以只更改最近更改时间和最近改动时间,而不会更改文件最近访问时间
如:echo "hello world" >> hello;然后stat hello
4、实现ls -l命令
需要使用struct passwd* getpwuid(uid_t uid);获得用户名,需要传入uid
1 struct passwd{
2 char *pw_name;/*username*/用户名
3 char *pw_passwd;/*user password*/
4 uid_t pw_uid;/*user ID*/
5 gid_t pw_gid;/*group ID*/
6 char *pw_gecos;/*user information*/
7 char *pw_dir;/*home directory*/
8 char *pw_shell;/*shell program*/
9 };
struct group *getgrgid(gid_t gid);获得组名
1 struct group{
2 char *gr_name;/*group name*/组名
3 char *gr_passwd;/*group password*/
4 gid_t gr_gid;/*group ID*/
5 char **gr_mem;/*NULL-terminated array of pointers to names of group members*/
6 };
获得本地时间 struct tm *localtime(const time_t *timep); 传入参数timep对应stat函数得到的结构体的秒数(time_t类型),返回tm结构体
1 struct tm{
2 int tm_sec;/*Seconds(0-60)*/秒
3 int tm_min;/*Minutes(0-59)*/分钟
4 int tm_hour;/*Hours(0-23)*/小时
5 int tm_mday;/*Day of the month(1-31)*/天
6 int tm_mon;/*Month(0-11)*/月,需要+1
7 int tm_year;/*Year-1900*/年,需要+1900
8 int tm_wday;/*Day of the week(0-6,Sunday=0)*/
9 int tm_yday;/*Day in the year(0-365,1 Jan = 0)*/
10 int tm_isdst;/*Daylight saving time*/
11 };
>touch ls_l.c
>vi ls_l.c
1 //-rwrwxr-x 2 wang wang 22 6月 30 10:19 hello
2 #include<stdio.h>
3 #include<sys/types.h>
4 #include<sys/stat.h>
5 #include<unistd.h>
6 #include<fcntl.h>
7 #include<string.h>
8 #include<time.h>
9 #include<pwd.h>
10 #include<grp.h>
11
12 int main(int argc, char* argv[])
13 {
14 if(argc != 2)
15 {
16 printf("./a.out filename\n");
17 return -1;
18 }
19 //调用stat,得到文件属性信息
20 struct stat sb;
21 stat(argv[1],&sb);
22 //解析属性信息,st_mode,uid,gid,time
23 //st_mode
24 char stmode[11] = {0};
25 memset(stmode,'-',sizeof(stmode)-1);
26 if(S_ISREG(sb.st_mode)) stmode[0]='-';//普通文件
27 if(S_ISDIR(sb.st_mode)) stmode[0]='d';
28 if(S_ISCHR(sb.st_mode)) stmode[0]='c';
29 if(S_ISBLK(sb.st_mode)) stmode[0]='b';
30 if(S_ISFIFO(sb.st_mode)) stmode[0]='p';
31 if(S_ISLNK(sb.st_mode)) stmode[0]='l';
32 if(S_ISSOCK(sb.st_mode)) stmode[0]='s';
33
34 //解析权限
35 if(sb.st_mode & S_IRUSR) stmode[1]='r';
36 if(sb.st_mode & S_IWUSR) stmode[2]='w';
37 if(sb.st_mode & S_IXUSR) stmode[3]='x';
38
39 if(sb.st_mode & S_IRGRP) stmode[4]='r';
40 if(sb.st_mode & S_IWGRP) stmode[5]='w';
41 if(sb.st_mode & S_IXGRP) stmode[6]='x';
42
43 if(sb.st_mode & S_IROTH) stmode[7]='r';
44 if(sb.st_mode & S_IWOTH) stmode[8]='w';
45 if(sb.st_mode & S_IXOTH) stmode[9]='x';
46
47 //分析用户名,组名可以通过函数获得getpwuid,getgrgid
48 //时间获取
49 struct tm *filetm = localtime(&sb.st_atim.tv_sec);
50 char timebuf[20]={0};
51 sprintf(timebuf,"%d月 %d %02d:%02d",filetm->tm_mon+1,filetm->tm_mday,
52 filetm->tm_hour,filetm->tm_min);
53
54 printf("%s %ld %s %s %ld %s %s\n",stmode,sb.st_nlink,getpwuid(sb.st_uid)->pw_name,getgrgid(sb.st_gid)->gr_name,sb.st_size,timebuf,argv[1]);
55 printf("-rwrwxr-x 2 wang wang 22 6月 30 10:19 hello\n");
56 return 0;
57 }
>make
>ls -lrt
>./ls_l ls_l.c
结果与ls -l ls_l.c进行对比
5、stat与lstat的区别
>ln -s hello hello.soft
>ls -lrt
>./ls_l hello.soft
问题:stat编写的ls_l函数在查看软链接时,对软件大小出现错误(显示的是原来文件的大小),穿透功能。
解决:更改为lstat函数
>make
>ls -lrt
>./ls_l hello.soft
注意:stat与lstat的区别:stat碰到链接,会追溯到源文件,穿透!!!lstat并不会穿透。
6、access与truncate
》access判断文件的权限是否存在
man 2 access
int access(const char *pathname, int mode);
pathname 文件
mode
R_OK
W_OK
X_OK
F_OK
返回值:如果有权限或者文件存在,对应返回0,失败返回-1,设置errno
>touch access.c
>vi access.c
1 #include<stdio.h>
2 #include<unistd.h>
3
4 int main(int argc, char* argv[])
5 {
6 if(argc != 2)
7 {
8 printf("./a.out filename\n");
9 return -1;
10 }
11
12 if(access(argv[1],R_OK)==0) printf("%s read ok!\n",argv[1]);
13 if(access(argv[1],W_OK)==0) printf("%s write ok!\n",argv[1]);
14 if(access(argv[1],X_OK)==0) printf("%s exe ok!\n",argv[1]);
15 if(access(argv[1],F_OK)==0) printf("%s file exit!\n",argv[1]);
16
17 return 0;
18 }
>gcc access.c
>./a.out hello
>sudo ./a.out hello
注意:结果不同,说明针对使用用户不同,看到的结果不一样!
》truncate截断文件
man 2 truncate
int truncate(const char *path, off_t length);
path 文件名
length 长度,如果大于源文件,直接拓展,如果小于源文件,截断
返回值:成功返回0,失败返回-1,设置errno
>touch truncate.c
>vi truncate.c
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<sys/types.h>
4
5 int main()
6 {
7 truncate("hello",1024);
8
9 return 0;
10 }
>make
>./truncate
>ls -lrt
7、readlink、unlink
》link——创建硬链接
man 2 link
int link(const char *oldpath, const char *newpath);
oldpath 源文件
newpath 硬链接文件
返回值:成功返回0,失败返回-1,设置errno
>touch link_symlink.c
>vi link_symlink.c
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<sys/types.h>
4
5 int main()
6 {
7 link("hello", "hello.hard1");
8
9 return 0;
10 }
>make
>./link_symlink
>ls -lrt
》symlink——创建软链接
man 2 symlink
int symlink(const char *target, const char *linkpath);
target 源文件
linkpath 软链接文件
返回值:成功返回0,失败返回-1,设置errno
>touch link_symlink.c
>vi link_symlink.c
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<sys/types.h>
4
5 int main()
6 {
7 //link("hello", "hello.hard1");
8 link("hello", "hello.soft1");
9
10 return 0;
11 }
>make
>./link_symlink
>ls -lrt
》readlink——读取符号(软)链接本身内容,得到链接指向的文件名
man 2 readlink
ssize_t readlink(const char *path, char *buf, size_t bufsiz);
pathname 链接名
buf 缓冲区
bufsiz 缓冲区大小
返回值:成功返回buf填充的大小,失败返回-1,设置errno
>touch readlink_unlink.c
>vi readlink_unlink.c
1 #include<stdio.h>
2 #include<unistd.h>
3
4 int main()
5 {
6 char buf[32]={0};
//readlink("hello.hard1", buf, sizeof(buf));//硬链接不行
7 readlink("hello.soft1", buf, sizeof(buf));
8 printf("buf is %s\n", buf);
9 return 0;
10 }
>make
>./readlink_unlink
》unlink——删除符号(软)链接或者是硬链接计数
man 2 unlink
int unlink(const char *pathname);
pathname 对应的链接名字,文件也可以
返回值:成功返回0,失败返回-1,设置errno
>touch readlink_unlink.c
>vi readlink_unlink.c
1 #include<stdio.h>
2 #include<unistd.h>
3
4 int main()
5 {
6 char buf[32]={0};
7 //readlink("hello.hard1", buf, sizeof(buf));//硬链接不行
8 readlink("hello.soft1", buf, sizeof(buf));
9 printf("buf is %s\n", buf);
10
11 unlink("hello.soft1");
12 unlink("hello.hard1");
13 return 0;
14 }
>make
>./readlink_unlink
8、unlink补充
如果文件后边需要用到,unlink先不删除,但是会写成功,进程结束后删除!
>touch unlink.c
>vi unlink.c
1 #include<stdio.h>
2 #include<sys/types.h>
3 #include<sys/stat.h>
4 #include<fcntl.h>
5 #include<unistd.h>
6 #include<string.h>
7
8 int main(int argc, char* argv[])
9 {
10 int fd = open("world",O_WRONLY|O_CREAT,0666);
11
12 unlink("world");
13
14 int ret = write(fd,"hello", 5);
15 if(ret > 0){
16 printf("write ok!%d\n", ret);
17 }
18 if(ret < 0){
19 perror("write err");
20 }
21
22 close(fd);
23 return 0;
24 }
>make
>./unlink
9、chown与rename
》chown——改变用户和组
man 2 chown
int chown(const char *pathname, uid_t owner, gid_t group);
pathname 文件名
owner 用户ID,/etc/passwd
group 组ID,/etc/group
》rename——重命名文件
man 2 rename
int rename(const char *oldpath, const char *newpath);
oldpath 旧文件
newpath 新文件
返回值:成功返回0,失败返回-1,设置errno
>touch rename.c
>vi rename.c
>mkdir aaa
1 #include<stdio.h>
2
3 int main()
4 {
5 //改文件名
6 //rename("a.out", "a.new");
7 //改目录
8 rename("aaa","bbb")
9
10 return 0;
11 }
>make
>./rename
目录相关函数
10、chdir、getcwd切换目录和获得工作路径
》getcwd——获得当前工作路径
man 2 getcwd
char *getcwd(char *buf, size_t size);
buf 传出参数,路径
size 缓冲区大小
返回值:成功返回路径的指针,失败返回NULL
》chdir——改变工作路径,注意属于进程独有的!
man 2 chdir
int chdir(const char *path);
path 对应的目标工作路径
返回值:成功返回0,失败返回-1,设置errno
>touch cwd_chdir.c
>vi cwd_chdir.c
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<sys/types.h>
4 #include<sys/stat.h>
5 #include<fcntl.h>
6
7 int main(int argc, char* argv[])
8 {
9 //先切换工作目录
10 chdir("bbb");
11
12 //留下点痕迹
13 int fd = open("temp",O_WRONLY|O_CREAT,0666);
14 write(fd, "daociyiyou", 10);
15 close(fd);
16 //显示当前工作目录
17 char buf[256];
18 getcwd(buf, sizeof(buf));
19
20 printf("buf is [%s]\n", buf);
21 close(fd);
22 return 0;
23 }
>make
>./cwd_chdir
11、mkdir创建目录
》mkdir——创建目录
man 2 mkdir
int mkdir(const char *pathname, mode_t mode);
pathname 路径
mode mode & ~umask(0777)注意:权限问题,如果目录没有可执行权限,不可进入
返回值:成功返回0,失败返回-1,设置errno
>touch mkdir.c
>vi mkdir.c
1 #include<stdio.h>
2 #include<sys/types.h>
3 #include<sys/stat.h>
4
5
6 int main(int argc, char* argv[])
7 {
8 if(argc != 2)
9 {
10 printf("./a.out filename\n");
11 return -1;
12 }
13
14 mkdir(argv[1],0777);
15 return 0;
16 }
>make
>./mkdir
12、读目录相关函数介绍
》rmdir——删除空目录
man 2 rmdir
int rmdir(const char *pathname);
需求:统计一下指定目录下普通文件的个数,要求子目录递归
(shell计数:find ./ -type f | wc -l)
》opendir——打开目录
man 2 opendir
DIR *opendir(const char *name);
name 打开的目录
返回值:成功返回DIR*的指针,指向目录项的信息,失败返回NULL
》readdir——读目录
man 2 readdir
struct dirent *readdir(DIR *dirp);
dirp 传入参数,opendir返回的指针
返回值:成功为读到目录项的内容,读到末尾或者有错误返回NULL
》closedir——关闭目录
man 2 closedir
int closedir(DIR *dirp);
dirp opendir得到的指针
13、递归子目录统计普通文件个数
>touch filecount.c
>vi filecount.c
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<dirent.h>
4 #include<sys/types.h>
5 #include<string.h>
6
7 int count = 0;//定义一个全局的计数
8
9 int DirCount(char *dirname)
10 {
11 printf("%s\n",dirname);
12 //打开目录
13 DIR *dirp = opendir(dirname);
14 if(dirp == NULL){
15 perror("opendir err");
16 return -1;
17 }
18 //循环读目录,如果是普通文件,count++,如果是目录,继续调用DirCount
19 struct dirent *dentp = NULL;
20 while((dentp = readdir(dirp))!= NULL){//如果为NULL,代表读到目录末尾
21 //printf("dirname:%s,dtype:%d\n",dentp->d_name,dentp->d_type);
22 if(dentp->d_type == DT_DIR){//如果是目录
23 if(strcmp(".",dentp->d_name)==0 || strcmp("..",dentp->d_name)==0){
24 continue;
25 }
26 //注意进程的工作路径,不能直接打开子目录
27 //使用dirname拼接下一级子目录
28 char newdirname[256]={0};
29 sprintf(newdirname,"%s/%s",dirname,dentp->d_name);
30 DirCount(newdirname);
31 }
32 if(dentp->d_type == DT_REG){
33 //普通文件,开始计数
34 count++;
35 printf("dname:%s\n",dentp->d_name);
36 }
37 }
38 //关闭目录
39 closedir(dirp);
40 return 0;
41 }
42
43 int main(int argc, char* argv[])
44 {
45 if(argc != 2)
46 {
47 printf("./a.out dirname\n");
48 return -1;
49 }
50
51 DirCount(argv[1]);
52 printf("count=%d\n",count);
53 //打开目录
54 struct DIR *dirp = opendir(argv[1]);
55 //循环读目录,判断如果是普通文件,计数++
56 readdir();
57 //关闭目录
58
59 return 0;
60 }
>make
>./filecount ./
>find ./ -type f | wc -l
(采用系统shell计数对比下二者计数是否相同)
14、errno说明
按全局变量理解,存储是错误信息
》查看vi /usr/include/asm-generic/errno.h
》查看vi /usr/include/asm-generic/errno-base.h
errno输出函数:可以用strerror打印出错误信息
man 2 strerror
char * strerror(int errnum);
15、dup2和dup说明
》dup2——重定向
man 2 dup2
int dup2(int oldfd, int newfd);
关闭newfd对应的文件描述符,将newfd重新指向为oldfd对应的文件
》dup——复制文件描述符
int dup(int oldfd);
新返回一个文件描述符指向oldfd对应的文件
16、dup2和dup的使用
需求:在代码中执行2次print("hello world\n");前一次输出到hello文件中,后一次输出到屏幕上。
>touch dup2du.c
>vi dup2du.c
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<sys/types.h>
4 #include<sys/stat.h>
5 #include<fcntl.h>
6
7 int main()
8 {
9 //先备份现场
10 int outfd = dup(1);
11 //然后做重定向
12 int fd = open("world",O_WRONLY|O_CREAT,0666);
13 dup2(fd,1);//将标准输出重定向到fd对应的文件
14 printf("hello world\n");
15
16 //需要来一次刷新
17 fflush(stdout);
18
19 //需要恢复1重新对应标准输出
20 dup2(outfd,1);
21
22
23 printf("hello world\n");
24 close(fd);
25 return 0;
26 }
>make
>./dup2du
拓:fcntl也可以复制文件描述符。
在学习Linux命令基础总结了笔记,并分享出来。