Linux命令基础——stat-readdir-dup2
在学习Linux命令基础总结了笔记,并分享出来。有问题请及时联系博主:Alliswell_WP,转载请注明出处。
08-linux-day04(stat-readdir-dup2)
目录:
一、学习目标
二、文件和目录操作
1、打开最大文件数量
2、stat函数介绍
3、stat函数介绍2与stat命令
4、实现ls -l命令
5、stat与lstat的区别
6、access与truncate
7、readlink、unlink
8、unlink补充
9、chown与rename
10、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命令基础总结了笔记,并分享出来。有问题请及时联系博主:Alliswell_WP,转载请注明出处。
posted on 2020-06-29 19:04 Alliswell_WP 阅读(359) 评论(0) 编辑 收藏 举报