目录的操作不论是在嵌入式产品开发还是应用软件编程都是必不可少的,这里我们主要是讨论在Linux平台下用C语言对目录的一系列主要操作:
1、获取当前目录操作:
在系统命令行下我们可以直接输入命令:pwd 来获取当前的工作目录,
在标准C库中也提供了相应的接口函数:
#include <unistd.h>
char * getcwd(char * buf,size_t size);
getcwd函数把当前目录的名字写到给定的缓冲区buf里。
如果目录的名字超出了参数size给出的缓冲区长度,它就返回NULL。
如果成功,它返回指针buf,我们可以访问buf来获取当前的目录。
范例
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(){
char buf[80]={0};
getcwd(buf,sizeof(buf));
printf("当前的目录位置: %s \n",buf);
return 0;
}
//编译执行
[root@localhost file]# gcc -g getcw.c -o getcw
[root@localhost file]# ./getcw
当前的目录位置: /home/samba/share/file
倘若参数buf 为NULL,getcwd()会依参数size 的大小自动配置内存(使用malloc()),
如果参数size 也为0,则getcwd()会依工作目录绝对路径的字符串长度来决定所配置的内存大小,
然后返回自动配置内存的指针,既然是malloc分配的堆内存,用完后记得要用free释放。
int main(){
/* char buf[80]={0};
getcwd(buf,sizeof(buf));
printf("当前的目录位置: %s \n",buf);
*/
char * pwd = getcwd(NULL,0);
printf("当前的目录位置: %s \n",pwd);
free(pwd);
return 0;
}
2.改变当前的工作目录:
我们在shell里使用cd命令来切换目录。
int chdir(const char *path);
在程序里则可以使用chdir函数调用来实现目录的变更,
一般情况下是配合 getcwd 函数一起使用来实现目录定位操作。
范例
//改变目录
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(){
char buf[80]={0};
getcwd(buf,sizeof(buf));
printf("当前的目录位置: %s \n",buf);
printf("改变一下目录: \n");
chdir("/tmp");
char buf2[80]={0};
getcwd(buf2,sizeof(buf2));
printf("当前的目录位置: %s \n",buf2);
return 0;
}
//编译执行
[root@localhost file]# gcc -g chdir.c -o chdir
[root@localhost file]# ./chdir
当前的目录位置: /home/samba/share/file
改变一下目录:
当前的目录位置: /tmp
3、目录的创建和删除:
在系统命令行下我们可以通过 “ mkdir” , “ rmdir” 命令通过Shell来实现帮我们创建一个目录和删除一个目录,
如果在实际的产品开发中,不可能我们自己去手动创建吧,
很多情况下都是通过程序自动创建一个目录来存放相应的文件。
创建目录:
头文件
#include <sys/stat.h>
#include <sys/types.h>
函数定义
int mkdir(const char * path,mode_t mode);
成功返回0,否则返回-1
- path
必需。
规定要创建的目录的名称。 - mode
规定权限。
mode 参数由四个数字组成:
第一个数字是 0,表示这个数是八进制数
第二个数字规定所有者的权限
第三个数字规定所有者所属的用户组的权限
第四个数字规定其他所有人的权限
可能的值(如需设置多个权限,请对下面的数字进行总计):
1 = 执行权限
2 = 写权限
4 = 读权限
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
int main(){
mkdir("./tt",0777); //如果是root用户创建的,该文件权限为755,若是普通用户则为775(其它用户没有写权)
return 0;
}
//编译执行
[g@localhost file]$ gcc -g mkdir.c -o mkdir
[g@localhost file]$ ./mkdir
[g@localhost file]$ ll
drwxrwxr-x 2 g g 6 5月 5 09:01 tt
需要注意的是:
- mkdir()函数创建路径的权限是其mode参数和进程的umask作用的结果,具体是先把umask(002和022)取反,再与mode相与的结果。mode&~umask
- 一般情况,root用户的umask值为022,那么我们调用mkdir()创建一个0777权限的路径,
创建出的路径权限是0755。(0777&(~022)) - 不同的用户默认的默认的umask值可能不一样,比如我的测试环境,root用户默认的umask值是0022,名为g(samba)的普通用户默认的mask值是0002
- 必须创建一个让所有用户拥有读写权限的路径时,需要将umask设置成0,
这样创建的路径的权限则完全由mkdir()的mode参数决定。 - 在一个比较大的项目中,如果为了添加路径修改了umask的值,建议创建完路径后将umask值还原,不然会出现权限问题。
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
int main(){
//mkdir("./tt",0777); //如果是root用户创建的,该文件权限为755,若是普通用户则为775(其它用户没有写权)
umask(0); //普通用户(g-samba的用户),设权限掩码值为0,即没有掩码了,后所设的权是具体的权了
mkdir("./bb",0777);
umask(0002); //还回系统默认的权限掩码值
return 0;
}
//编译执行
[g@localhost file]$ gcc -g mkdir.c -o mkdir
[g@localhost file]$ ./mkdir
[g@localhost file]$ ll
drwxrwxrwx 2 g g 6 5月 5 09:06 bb
删除目录:
int rmdir(const char *path);
4、 打开、读取和关闭目录:
打开目录:
头文件:
#include <sys/types.h>
#include <dirent.h>
函数原型:
DIR * opendir(const char * name);
opendir()用来打开参数name 指定的目录, 并返回一个目录结构指针DIR*表示目录流, 和打开文件的函数open()类似, 接下来对目录的读取和搜索都要使用此返回值.
读取目录函数:
头文件:
#include <sys/types.h>
#include <dirent.h>
函数原型:
struct dirent * readdir(DIR * dir);
readdir()返回参数dir代表的目录流的下一个进入点。
这个点对应目录结构体:
struct dirent{
ino_t d_ino; /* inode number(此目录进入点的inode) */
off_t d_off; /* offset to the next dirent(目录文件开头到此子目录/子文件进入点的位移 */
unsigned short d_reclen; /* length of this record(目录名的长度) */
unsigned char d_type; /* type of file(所指的文件类型) */
char d_name[256]; /* filename(文件名) */
};
通过访问结构体中的 “d_type” 和 “d_name” 变量可以非常方便的实现文件的查找,文件的访问,文件的过滤。
d_type
- DT_BLK 块设备
- DT_CHR 字符设备
- DT_DIR 目录
- DT_LNK 软链接
- DT_FIFO 管道
- DT_REG 普通文件
- DT_SOCK 套接字
- DT_UNKNOWN 未知
范例1:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <dirent.h>
#include <sys/types.h>
//DIR目录的打开,读取与关闭,以前学的fopen是文件的打开
int main(){
DIR * dir = NULL;
struct dirent *ptr = NULL; //保存目录信息的结构体dirent的指针变量ptr
//打开目录
dir = opendir("./tt");
//读取目录
while((ptr=readdir(dir))!=NULL){
printf("d_name: %s and d_type: %d\n",ptr->d_name,ptr->d_type);
}
closedir(dir);
return 0;
}
//编译执行
[g@localhost file]$ gcc -g diropen.c -o diropen
[g@localhost file]$ ./diropen
d_name: . and d_type: 4
d_name: .. and d_type: 4
d_name: subdir and d_type: 4
d_name: 1.txt and d_type: 8
d_name: 2.txt and d_type: 8
递归遍历目录下的所有文件:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <dirent.h>
#include <sys/types.h>
#include <string.h>
//文件目录遍历
//递归实现遍历目标目录下的所有文件
int readFileList(char * basePath){
DIR * dir = NULL;
struct dirent * ptr = NULL;
char base[1000] ={0};
//打开目录
if((dir = opendir(basePath))==NULL){
printf("打开目录失败!");
exit(1);
}
//读取目录
while((ptr = readdir(dir))!=NULL){
//过滤.和..两个目录
if(strcmp(ptr->d_name,".") ==0 || strcmp(ptr->d_name,"..") ==0){
continue; //终止本次循环,执行下一次
}else if(ptr->d_type == DT_REG){//剩下的情况:要么是文件(我们要的),要么是目录(递旨进行找到文件)
printf("d_name: %s/%s\n",basePath,ptr->d_name); //是普通文件,我们要的
}else if(ptr->d_type == DT_LNK){ //如果是链接文件,打印一下
printf("d_name: %s/%s\n",basePath,ptr->d_name);
}else if(ptr->d_type == DT_DIR){ // 如果是目录文件,递归
strcpy(base,basePath);
strcat(base,"/");
strcat(base,ptr->d_name);
readFileList(base); //递归调用readFileList,遍历子目录
}
}
closedir(dir);
return 1;
}
int main(){
readFileList("./tt");
return 0;
}
//编译执行
[g@localhost file]$ gcc -g dirbianli.c -o dirbianli
[g@localhost file]$ ./dirbianli
d_name: ./tt/subdir/3.txt
d_name: ./tt/subdir/4.txt
d_name: ./tt/subdir/5.txt
d_name: ./tt/1.txt
d_name: ./tt/2.txt
目录关闭:
int closedir(DIR *dpt);
5、重设读取目录的位置为开头位置
头文件:
#include <sys/types.h>
#include <dirent.h>
定义函数:
void rewinddir(DIR *dir);
函数说明:rewinddir()用来设置参数dir 目录流目前的读取位置为原来开头的读取位置.
范例:把当前目录下的tt目录下的文件打印两遍,这里不递归子目录了
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <dirent.h>
#include <sys/types.h>
int main(){
DIR * dir = NULL;
struct dirent * ptr = NULL;
dir = opendir("./tt");
while((ptr=readdir(dir))!=NULL){
printf("d_name:%s \n",ptr->d_name);
}//这个循环完毕后,位置指针就在目录结尾外,想要重新读取一遍,必须把位置重置一下
rewinddir(dir);
while((ptr=readdir(dir))!=NULL){
printf("d_name:%s \n",ptr->d_name);
}
closedir(dir);
return 0;
}
//把当前目录下的tt目录下的文件打印两遍,这里不递归子目录了
6、取得目录流的读取位置
头文件:
#include <dirent.h>
定义函数:
off_t telldir(DIR *dir); //off_t偏移量类型,实际是一个长整形数据
函数说明:telldir()返回参数dir 目录流目前的读取位置. 此返回值代表距离目录文件开头的偏移量, 有错误发生时返回-1,这偏移量在配合下面的seekdir函数可用重新读取指定位置的子文件。
范例:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <dirent.h>
#include <sys/types.h>
int main(){
DIR * dir = NULL;
struct dirent * ptr = NULL;
dir = opendir("./tt");
off_t offset; //偏移量类型变量,实际是一个长整形数据
while((ptr=readdir(dir))!=NULL){
//当前位置指针指向的位置,获取出来
offset = telldir(dir);
printf("d_name:%s %ld\n",ptr->d_name,offset);
}
rewinddir(dir);
while((ptr=readdir(dir))!=NULL){
printf("d_name:%s \n",ptr->d_name);
}
closedir(dir);
return 0;
}
//编译执行
[root@localhost file]# gcc -g off_tdir.c -o off_tdir
[root@localhost file]# ./off_tdir
d_name:. 10
d_name:.. 12
d_name:1.txt 15
d_name:2.txt 18
d_name:subdir 512
d_name:.
d_name:..
d_name:1.txt
d_name:2.txt
d_name:subdir
7、设置读取目录的位置
头文件:
#include <dirent.h>
定义函数:
void seekdir(DIR * dir, off_t offset);
函数说明:seekdir()用来设置参数dir 目录流目前的读取位置, 在调用readdir()时便从此新位置开始读取. 参数offset 代表距离目录文件开头的偏移量。
范例:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <dirent.h>
#include <sys/types.h>
//第一次读取目录tt下的所有文件,第二次读从第四个文件开始读取打印
int main(){
DIR * dir = NULL;
struct dirent * ptr = NULL;
dir = opendir("./tt");
off_t offset; //偏移量类型变量,实际是一个长整形数据
off_t offset3;
int i=0;
while((ptr=readdir(dir))!=NULL){
//当前位置指针指向的位置,获取出来
offset = telldir(dir);
i++;
if(i==3)
offset3 = offset;
printf("d_name:%s %ld\n",ptr->d_name,offset);
}
printf("第三个文件位置的偏移量:%ld\n",offset3);
seekdir(dir,offset3); //从0开始算呀
while((ptr=readdir(dir))!=NULL){
printf("d_name:%s \n",ptr->d_name);
}
closedir(dir);
return 0;
}
//编译执行
[root@localhost file]# gcc -g seekdir.c -o seekdir
[root@localhost file]# ./seekdir
d_name:. 10
d_name:.. 12
d_name:1.txt 15
d_name:2.txt 18
d_name:subdir 512
第四个文件位置的偏移量:15
d_name:2.txt
d_name:subdir
8、取得用户识别码
头文件:
#include <unistd.h>
#include <sys/types.h>
定义函数:
uid_t getuid(void);
函数说明:getuid()用来取得执行目前进程的用户识别码。
返回值:用户识别码
范例:
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
main(){
printf("uid is %d\n", getuid());
return 0;
}
//编译执行
[root@localhost userinfo]# gcc -g getuid.c -o getuid
[root@localhost userinfo]# ./getuid
uid is 0 //0是用户root的用户id,可由 cat /etc/passwd查看到
9、取得组识别码函数
头文件:
#include <unistd.h>
#include <sys/types.h>
定义函数:
gid_t getgid(void);
函数说明:getgid()用来取得执行目前进程的组识别码。
返回值:返回组识别码
范例:
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
main()
{
printf("gid is %d\n", getgid());
return 0;
}
10、通过uid获取用户信息
头文件:
#include <pwd.h>
#include <sys/types.h>
定义函数:
struct passwd * getpwuid(uid_t uid);
函数说明:getpwuid()用来逐一搜索参数uid 指定的用户识别码, 找到时便将该用户的数据以结构体返回。
passwd 结构定义如下:
struct passwd
{
char * pw_name; //用户账号
char * pw_passwd; //用户密码 一般情部分下用不到,获取的也不是明文
uid_t pw_uid; //用户识别码
gid_t pw_gid; //组识别码
char * pw_gecos; //用户全名
char * pw_dir; //家目录
char * pw_shell; //所使用的shell路径
};
返回值:返回 passwd 结构数据, 如果返回NULL 则表示已无数据, 或者有错误发生.
范例:
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <pwd.h>
int main(){
struct passwd * user;
user = getpwuid(1002); //1002samba用户g的id
printf("username: %s \n",user->pw_name);
printf("uid:%d\n", user->pw_uid);
printf("home:%s\n", user->pw_dir);
return 0;
}
//编译执行
[root@localhost userinfo]# gcc -g getuserinfo.c -o user
[root@localhost userinfo]# ./user
username: g
uid:1002
home:/home/g
11、通过用户名获取用户信息
头文件:
#include <pwd.h>
#include <sys/types.h>
定义函数:
struct passwd * getpwnam(const char * name);
函数说明:getpwnam()用来逐一搜索参数name 指定的账号名称, 找到时便将该用户的数据以passwd 结构返回。
返回值:返回 passwd 结构数据,如果返回NULL 则表示已无数据, 或有错误发生.
范例:
/*取得root 账号的识别码和根目录 */
#include <pwd.h>
#include <sys/types.h>
#include <stdio.h>
int main()
{
struct passwd *user;
user = getpwnam("root");
printf("name:%s\n", user->pw_name);
printf("uid:%d\n", user->pw_uid);
printf("home:%s\n", user->pw_dir);
return 0;
}
12、通过gid获取用户组信息
头文件:
#include <grp.h>
#include <sys/types.h>
定义函数:
strcut group * getgrgid(gid_t gid);
函数说明:getgrgid()用来依参数gid 指定的组识别码逐一搜索组文件, 找到时便将该组的数据以group 结构返回.
group 结构:
struct group
{
char *gr_name; //组名称
char *gr_passwd; //组密码,一般情部分下用不到,获取的也不是明文
gid_t gr_gid; //组识别码
char **gr_mem; //组成员账号
}
返回值:返回 group 结构数据, 如果返回NULL 则表示已无数据, 或有错误发生.
范例
/* 取得gid=3 的组数据 */
#include <grp.h>
#include <sys/types.h>
int main()
{
struct group *data;
int i = 0;
data = getgrgid(3);
printf("%s:%s:%d:", data->gr_name, data->gr_passwd, data->gr_gid);
while (data->gr_mem[i]) printf("%s, ", data->gr_mem[i++]);
printf("\n");
return 0;
}
13、通过用户组名获取用户组信息
头文件:
#include <grp.h>
#include <sys/types.h>
定义函数:
strcut group * getgrnam(const char * name);
函数说明:getgrnam()用来逐一搜索参数那么指定的组名称, 找到时便将该组的数据以group 结构返回。
返回值:返回 group 结构数据, 如果返回NULL 则表示已无数据, 或有错误发生.
范例
/* 取得adm 的组数据 */
#include <grp.h>
#include <sys/types.h>
#include <stdio.h>
int main()
{
struct group *data;
int i = 0;
// data = getgrgid(3);
data = getgrnam("sys");
printf("%s:%s:%d:", data->gr_name, data->gr_passwd, data->gr_gid);
while (data->gr_mem[i]) printf("%s, ", data->gr_mem[i++]);
printf("\n");
return 0;
}
14、获取当前时间(以秒数表示)
头文件:
#include <time.h>
定义函数:
time_t time(time_t *t);
函数说明:此函数会返回从公元 1970 年1 月1 日的UTC 时间从0 时0 分0 秒算起到现在所经过的秒数。
如果t 并非空指针的话,此函数也会将返回值存到t 指针所指的内存。
返回值:成功则返回秒数,失败则返回((time_t)-1)值,错误原因存于errno 中。
范例
#include <stdio.h>
#include <time.h>
#include <stdio.h>
int main() {
int seconds = time((time_t*)NULL);
printf("%d\n", seconds); //1714961881毫秒 1970-1-1 0:0到 2024年5月6日 10:18
return 0;
}
15、将时间日期以字符串格式表示
头文件:
#include <time.h>
定义函数:
char *ctime(const time_t *timep);
函数说明:ctime()将参数timep 所指的time_t 结构中的信息转换成真实世界所使用的时间日期表示方法,然后将结果以字符串形态返回。
此函数已经由时区转换成当地时间,字符串格式为"Wed Jun 30 21 :49 :08 1993\n"。
返回值:返回一字符串表示目前当地的时间(已把格时转化为了当地的)日期。
范例
#include <stdio.h>
#include <time.h>
int main() {
time_t timep;
time (&timep);
printf("%s", ctime(&timep));
return 0;
}
16、获取当前时间和日期到结构体
头文件:
#include <time.h>
定义函数:
struct tm *gmtime(const time_t *timep);
函数说明:gmtime()将参数timep 所指的time_t 结构中的信息转换成真实世界所使用的时间日期表示方法,然后将结果由结构tm返回。
结构tm 的定义为
struct tm{
int tm_sec; //代表目前秒数, 正常范围为0-59
int tm_min; //代表目前分数, 范围0-59
int tm_hour; //从午夜算起的时数, 范围为0-23
int tm_mday; //目前月份的日数, 范围01-31
int tm_mon; //代表目前月份, 从一月算起, 范围从0-11
int tm_year; //从1900 年算起至今的年数
int tm_wday; //一星期的日数, 从星期一算起, 范围为0-6
int tm_yday; //从今年1 月1 日算起至今的天数, 范围为0-365
int tm_isdst; //日光节约时间的旗标
//日光节约时间的旗标——外国人的夏令时
//夏季时将时钟拨快一小时,
//因为夏天太阳日落很晚,
//所以平常晚上八点天还是很亮,将时间拨快,就变成九点(其实已经八点),但这样可以节省能源,
// 所以每年四月第一个星期日将时间拨快一小时,十月最后一个星期日将时间拨回。
};
此函数返回的时间日期未经时区转换,而是UTC 时间。
返回值:返回结构tm 代表目前UTC 时间。
范例
#include <stdio.h>
#include <time.h>
int main() {
char *wday[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
time_t timep;
struct tm *p;
time(&timep);
p = gmtime(&timep);
printf("%d-%d-%d ", (1900 + p->tm_year), (1 + p->tm_mon), p->tm_mday);
printf("%d:%d:%d %s \n", p->tm_hour, p->tm_min, p->tm_sec, wday[p->tm_wday]);
return 0;
}
//编译执行
PS Y:\userinfoostime> gcc -g curtime.c -o curtime.exe
PS Y:\userinfoostime> ./curtime
2024-5-6 2:28:17 Mon //很恶心,现在是北京10点,但这里是2点,我们比国际标准时间早了8小时
17、当前时间和日期并转换为本地时间
头文件:
#include <time.h>
定义函数:
struct tm *localtime(const time_t * timep);
函数说明:localtime()将参数timep 所指的time_t 结构中的信息转换成真实世界所使用的时间日期表示方法,然后将结果由结构tm 返回。结构tm 的定义请参考gmtime()。此函数返回的时间日期已经转换成当地时区。
返回值:返回结构tm 代表目前的当地时间。
范例
#include <stdio.h>
#include <time.h>
int main() {
char *wday[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
time_t timep;
struct tm *p;
time(&timep);
p = localtime(&timep);
printf("%d-%d-%d ", (1900 + p->tm_year), (1 + p->tm_mon), p->tm_mday);
printf("%d:%d:%d %s \n", p->tm_hour, p->tm_min, p->tm_sec, wday[p->tm_wday]);
return 0;
}
//编译执行
PS Y:\userinfoostime> gcc -g curtime.c -o curtime.exe
PS Y:\userinfoostime> ./curtime
2024-5-6 10:28:17 Mon //上面的tm *getime()返回的是2:28:17
18、将时间转换成经过的秒数
头文件:
#include <time.h>
定义函数:
time_t mktime(strcut tm * timeptr);
函数说明:mktime()用来将参数timeptr 所指的tm 结构数据转换成从公元1970 年1 月1 日0 时0 分0 秒算起至今的UTC 时间所经过的秒数。
返回值:返回经过的秒数。
范例:用time()取得时间 (秒数), 利用localtime() 转换成struct tm 再利用mktime()将struct tm 转换成原来的秒数。
#include <stdio.h>
#include <time.h>
int main() {
time_t timep;
struct tm *p;
time(&timep);
printf("time() : %d \n", timep);
p = localtime(&timep);
timep = mktime(p);
printf("time()->localtime()->mktime():%d\n", timep);
return 0;
}
//编译执行
PS Y:\userinfoostime> gcc -g mktime.c -o mktime.exe
PS Y:\userinfoostime> ./mktime
time() : 1714963345
time()->localtime()->mktime():1714963345 //秒数,与上面14中time()获取的一样