文件系统操作函数
一个进程启动之后,默认打开三个文件描述符:
#define STDIN_FILENO 0
#define STDOUT_FILENO 1
#define STDERR_FILENO 2
新打开文件返回文件描述符表中未使用的最小文件描述符,调用open函数可以打开或创建一个文件,得到一个文件描述符。
perror和errno
errno是一个全局变量,当系统调用后若出错会将errno进行设置,perror可以将errno对应的描述信息打印出来。
如:perror("open");
如果报错的话打印:open:(空格)错误信息
练习:编写简单的例子,测试perror和errno。
#include <fcntl.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
int main() {
//打开一个不存在的文件
int fd = open("a.txt", O_RDWR);
if (fd < 0) {
//使用perror打印错误信息
perror("open faild");
return -1;
}
close(fd);
return 0;
}
文件操作函数
open
打开或者新建一个文件
函数原型:
c#include <fcntl.h> #include <sys/stat.h> #include <sys/types.h> int open(const char *pathname, int flags); int open(const char *pathname, int flags, mode_t mode);
函数参数:
pathname
参数是要打开或创建的文件名,和fopen一样,pathname既可以是相对路径也可以是绝对路径。flags
参数有一系列常数值可供选择,可以同时选择多个常数用按位或运算符连接起来,所以这些常数的宏定义都以O_开头表示or,必选项。以下三个常数中必须指定一个,且仅允许指定一个。O_RDONLY
只读打开O_WRONLY
只写打开O_RDWR
可读可写打开- 以下可选项可以同时指定0个或多个,和必选项按位或起来作为
flags
参数。可选项有很多,这里只介绍几个常用选项:O_APPEND
表示追加。如果文件已有内容,这次打开文件所写的数据附加到文件的末尾而不覆盖原来的内容。O_CREAT
若此文件不存在则创建它。使用此选项时需要提供第三个参数mode
,表示该文件的访问权限。可以使用 0xxx 来指定,比如 0755,不能不写 0 ,否则权限可能不如预期。- 文件最终权限:mode & ~umask
O_EXCL
如果同时指定了O_CREAT
,并且文件已存在,则出错返回。O_TRUNC
如果文件已存在,将其长度截断为为0字节。O_NONBLOCK
对于设备文件,以O_NONBLOCK
方式打开可以做非阻塞I/O(NonblockI/O),非阻塞I/O。
函数返回值:
- 成功:返回一个最小且未被占用的文件描述符
- 失败:返回
-1
,并设置errno
值
查看在线 manual: Debian
close
关闭文件
函数原型:
#include <unistd.h>
int close(int fd);
函数参数:
fd
文件描述符
函数返回值:
- 成功返回
0
- 失败返回
-1
,并设置errno
值
需要说明的是:当一个进程终止时,内核对该进程所有尚未关闭的文件描述符调用close
关闭,所以即使用户程序不调用close
,在终止时内核也会自动关闭它打开的所有文件。但是对于一个长年累月运行的程序(比如网络服务器),打开的文件描述符一定要记得关闭,否则随着打开的文件越来越多,会占用大量文件描述符和系统资源。
read
从打开的设备或文件中读取数据
函数原型:
#include <unistd.h>
ssize_t read(int fd, void* buf, size_t count);
函数参数:
fd
:文件描述符buf
:读上来的数据保存在缓冲区buf中count
:buf缓冲区存放的最大字节数
函数返回值:
>0
:读取到的字节数=0
:文件读取完毕-1
: 出错,并设置errno
write
向打开的设备或文件中写数据
函数原型:
#include <unistd.h>
ssize_t write(int fd, const void* buf, size_t count);
函数参数:
fd
:文件描述符buf
:缓冲区,要写入文件或设备的数据count
:buf
中数据的长度
函数返回值:
- 成功:返回写入的字节数
- 错误:返回
-1
并设置errno
lseek
所有打开的文件都有一个当前文件偏移量(current file offset),以下简称为cfo。 cfo通常是一个非负整数,用于表明文件开始处到文件当前位置的字节数。 读写操作通常开始于 cfo,并且使 cfo 增大,增量为读写的字节数。 文件被打开时,cfo 会被初始化为 0,除非使用了 O_APPEND
。
使用 lseek
函数可以改变文件的 cfo。
- 移动文件指针
函数原型:
#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
函数参数:
fd
:文件描述符- 参数
offset
的含义取决于参数whence
:- 如果 whence 是
SEEK_SET
,文件偏移量将设置为 offset。 - 如果 whence 是
SEEK_CUR
,文件偏移量将被设置为 cfo 加上 offset,offset 可以为正也可以为负。 - 如果 whence 是
SEEK_END
,文件偏移量将被设置为文件长度加上 offset,offset 可以为正也可以为负。
- 如果 whence 是
函数返回值:
- 若lseek成功执行,则返回新的偏移量。
lseek函数常用操作
- 文件指针移动到头部
lseek(fd, 0, SEEK_SET);
- 获取文件指针当前位置
int len = lseek(fd, 0, SEEK_CUR);
- 获取文件长度
int len = lseek(fd, 0, SEEK_END);
- lseek实现文件拓展
off_t currpos;
// 从文件尾部开始向后拓展1000个字节
currpos = lseek(fd, 1000, SEEK_END);
// 额外执行一次写操作,否则文件无法完成拓展
write(fd, “a”, 1);// 数据随便写
练习:
1、编写简单的IO函数读写文件的代码
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int main(void) {
//int open(const char *pathname, int flags, mode_t mode);
int fd = open("log.txt", O_RDWR | O_CREAT, 0777);
//ssize_t write(int fd, const void *buf, size_t count);
write(fd, "hello world!", strlen("hello world!"));
lseek(fd, 0, SEEK_SET);
//ssize_t read(int fd, void *buf, size_t count);
char buf[1024];
memset(buf, 0x00, sizeof(buf));
int ret = read(fd, buf, sizeof(buf));
printf("ret=[%d],buf=[%s]\n", ret, buf);
close(fd);
return 0;
}
2、使用lseek函数获取文件大小
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int main() {
int fd = open("./src/002_lseek_test.txt", O_RDWR | O_CREAT, 0755);
if (fd < 0) {
perror("Open error");
return -1;
}
char content[] = "这是Linux系统编程部分IO-lseek函数的测试文档\n用来测试lseek函数读取文件大小";
write(fd, content, strlen(content));
lseek(fd, 0, SEEK_SET);
int len = lseek(fd, 0, SEEK_END);
printf("文件大小是:[%d]\n", len);
close(fd);
return 0;
}
3、使用lseek函数实现文件拓展
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int main() {
int fd = open("./src/003_lseek_test.txt", O_RDWR | O_CREAT, 0755);
if (fd < 0) {
perror("Open error");
return -1;
}
char content[] = "这是Linux系统编程部分IO-lseek函数的测试文档\n用来测试lseek函数实现文件扩展";
write(fd, content, strlen(content));
lseek(fd, 0, SEEK_SET);
int len = lseek(fd, 0, SEEK_END);
printf("文件大小是:[%d]\n", len);
off_t curr;
curr = lseek(fd, 999, SEEK_END);
write(fd, "\n", 1);
lseek(fd, 0, SEEK_SET);
len = lseek(fd, 0, SEEK_END);
printf("扩展后文件大小是:[%d]\n", len);
close(fd);
return 0;
}
stat/lstat
获取文件属性
Windows平台参考手册: _stat, _wstat功能,Linux平台参考手册: man/lstat.2
函数原型:
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int stat(const char* pathname, struct stat* statbuf);
int fstat(int fd, struct stat* statbuf);
int lstat(const char* pathname, struct stat* statbuf);
函数返回值:
- 成功返回
0
- 失败返回
-1
struct stat结构体
struct stat {
dev_t st_dev; //文件的设备编号
ino_t st_ino; //节点
mode_t st_mode; //文件的类型和存取的权限
nlink_t st_nlink; //连到该文件的硬连接数目,刚建立的文件值为1
uid_t st_uid; //用户ID
gid_t st_gid; //组ID
dev_t st_rdev; //(设备类型)若此文件为设备文件,则为其设备编号
off_t st_size; //文件字节数(文件大小)
blksize_t st_blksize; //块大小(文件系统的I/O 缓冲区大小)
blkcnt_t st_blocks; //块数
time_t st_atime; //最后一次访问时间
time_t st_mtime; //最后一次修改时间
time_t st_ctime; //最后一次改变时间(指属性)
};
st_mode
是一个16位整数,其中:
- 0-2 bit -- 其他人权限c
S_IROTH 00004 //读权限 S_IWOTH 00002 //写权限 S_IXOTH 00001 //执行权限 S_IRWXO 00007 //掩码,过滤 st_mode中除其他人权限以外的信息
- 3-5 bit -- 所属组权限c
S_IRGRP 00040 //读权限 S_IWGRP 00020 //写权限 S_IXGRP 00010 //执行权限 S_IRWXG 00070 //掩码,过滤 st_mode中除所属组权限以外的信息
- 6-8 bit -- 文件所有者权限c
S_IRUSR 00400 //读权限 S_IWUSR 00200 //写权限 S_IXUSR 00100 //执行权限 S_IRWXU 00700 //掩码,过滤 st_mode中除文件所有者权限以外的信息 if (st_mode & S_IRUSR) //为真表明可读 if (st_mode & S_IWUSR) //为真表明可写 if (st_mode & S_IXUSR) //为真表明可执行
- 12-15 bit -- 文件类型c
S_IFSOCK 0140000 //套接字 S_IFLNK 0120000 //符号链接(软链接) S_IFREG 0100000 //普通文件 S_IFBLK 0060000 //块设备 S_IFDIR 0040000 //目录 S_IFCHR 0020000 //字符设备 S_IFIFO 0010000 //管道 S_IFMT 0170000 //掩码,过滤 st_mode中除文件类型以外的信息 if ((st_mode & S_IFMT)==S_IFREG) //为真普通文件 if(S_ISREG(st_mode)) //为真表示普通文件 if(S_ISDIR(st.st_mode)) //为真表示目录文件
stat和lstat的区别
- 对于普通文件,这两个函数没有区别,是一样的。
- 对于链接文件,调用lstat函数获取的是链接文件本身的属性信息;而stat函数获取的是链接文件指向的文件的属性信息。
文件权限计算方法
mode & ~umask
练习
1、stat函数获取文件大小
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int main() {
int fd = open("./src/004_stat_test.txt", O_RDWR | O_CREAT, 0755);
if (fd < 0) {
perror("Open faild");
return -1;
}
char content[] = "这是Linux系统编程部分IO-stat函数的测试文档\n用来测试stat函数获取文件大小";
write(fd, content, strlen(content));
struct stat st;
stat("./src/004_stat_test.txt", &st);
printf("size=[%d]\n", (int) st.st_size);
close(fd);
return 0;
}
2、stat函数获取文件类型和文件权限
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int main() {
int fd = open("./src/005_stat_test.txt", O_RDWR | O_CREAT, 0755);
if (fd < 0) {
perror("Open faild");
return -1;
}
char content[] = "这是Linux系统编程部分IO-stat函数的测试文档\n用来测试stat函数获取文件类型和权限";
write(fd, content, strlen(content));
struct stat st;
stat("./src/005_stat_test.txt", &st);
if (S_ISDIR(st.st_mode)) printf("目录文件");
else if (S_ISREG(st.st_mode))
printf("普通文件");
printf("\t其他人权限:[");
if (st.st_mode & S_IROTH) {
printf("r");
} else {
printf("-");
}
if (st.st_mode & S_IWOTH) {
printf("w");
} else {
printf("-");
}
if (st.st_mode & S_IXOTH) {
printf("x");
} else {
printf("-");
}
printf("]\n");
close(fd);
return 0;
}
3、lstat函数获取链接文件的属性(文件大小)
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int main() {
int fd = open("./src/006_lstat_test.txt", O_RDWR | O_CREAT, 0755);
if (fd < 0) {
perror("Open faild");
return -1;
}
char content[] = "这是Linux系统编程部分IO-stat函数的测试文档\n用来测试stat函数获取文件类型和权限";
write(fd, content, strlen(content));
struct stat st;
// int ret = stat("./src/006_lstat_test.txt.s", &st);
int ret = lstat("./src/006_lstat_test.txt.s", &st);
if (ret < 0) {
perror("Open faild(lstat)");
return -1;
}
if (S_ISDIR(st.st_mode))
printf("目录文件");
else if (S_ISLNK(st.st_mode))
printf("链接文件");
else if (S_ISREG(st.st_mode))
printf("普通文件");
printf("\tsize:[%ld]\t其他人权限:[", st.st_size);
if (st.st_mode & S_IROTH) {
printf("r");
} else {
printf("-");
}
if (st.st_mode & S_IWOTH) {
printf("w");
} else {
printf("-");
}
if (st.st_mode & S_IXOTH) {
printf("x");
} else {
printf("-");
}
printf("]\n");
close(fd);
return 0;
}
dup
函数描述
- 复制文件描述符
函数原型
#include <unistd.h>
int dup(int oldfd);
函数参数
oldfd
-要复制的文件描述符
函数返回值
- 成功:返回最小且没被占用的文件描述符
- 失败:返回-1,设置errno值
练习:编写程序,测试dup函数。
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int main() {
int fd = open("./src/007_dup_test.txt", O_RDWR | O_CREAT, 0755);
if (fd < 0) {
perror("fd open faild");
return -1;
}
int newfd = dup(fd);
if (newfd < 0) {
perror("newfd open faild");
return -1;
}
char content[] = "hello newfd~\nAre you ok?";
// write(fd, content, sizeof(content));
write(fd, content, strlen(content) + 1);
close(fd);
lseek(newfd, 0, SEEK_SET);
char buf[1024];
memset(buf, 0x00, sizeof(buf));
int ret = read(newfd, buf, sizeof(buf));
printf("ret=[%d],buf=[%s]\n", ret, buf);
return 0;
}
dup2
函数描述
- 复制文件描述符
函数原型
#include <unistd.h>
int dup2(int oldfd, int newfd);
函数参数
oldfd
-原来的文件描述符newfd
-复制成的新的文件描述符
函数返回值
- 成功:将oldfd复制给newfd,两个文件描述符指向同一个文件
- 失败:返回-1,设置errno值
- 假设newfd已经指向了一个文件,首先close原来打开的文件,然后newfd指向oldfd指向的文件。
若newfd没有被占用,newfd指向oldfd指向的文件。
练习
1、编写程序,测试dup2函数实现文件描述符的复制
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int main() {
int fd = open("./src/008_dup2_test.txt", O_RDWR | O_CREAT, 0755);
if (fd < 0) {
perror("fd open faild");
return -1;
}
int newfd;
dup2(fd, newfd);
if (newfd < 0) {
perror("newfd open faild");
return -1;
}
char content[] = "hello newfd~\nAre you ok?";
// write(fd, content, sizeof(content));
write(fd, content, strlen(content) + 1);
close(fd);
lseek(newfd, 0, SEEK_SET);
char buf[1024];
memset(buf, 0x00, sizeof(buf));
int ret = read(newfd, buf, sizeof(buf));
printf("ret=[%d],buf=[%s]\n", ret, buf);
return 0;
}
2、编写程序,完成终端标准输出重定向到文件中
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int main() {
int fd = open("./src/009_dup2_test.txt", O_RDWR | O_CREAT, 0755);
if (fd < 0) {
perror("fd open faild");
return -1;
}
int ret = dup2(fd, STDOUT_FILENO);
if (ret < 0) {
perror("dup2 running faild");
return -1;
}
printf("dup2 test\n");
return 0;
}
fcntl
函数描述
- 改变已经打开的文件的属性
函数原型
#include <unistd.h>
#include <fcntl.h>
int fcntl(int fd, int cmd, ... /* arg */);
函数参数
- 若
cmd
为F_DUPFD
,复制文件描述符,与dup相同 - 若
cmd
为F_GETFL
,获取文件描述符的flag属性值 - 若
cmd
为F_SETFL
,设置文件描述符的flag属性
函数返回值
- 返回值取决于cmd
- 成功
- 若
cmd
为F_DUPFD
,返回一个新的文件描述符 - 若
cmd
为F_GETFL
,返回文件描述符的flags值 - 若
cmd
为F_SETFL
,返回0
- 若
- 失败返回-1,并设置errno值。
fcntl函数常用的操作
1、复制一个新的文件描述符
int newfd = fcntl(fd, F_DUPFD, 0);
2、获取文件的属性标志
int flag = fcntl(fd, F_GETFL, 0);
3、设置文件状态标志
int flag = fcntl(fd, F_GETFL, 0);
flag = flag | O_APPEND;
fcntl(fd, F_SETFL, flag)
4、常用的属性标志
O_APPEND //设置文件打开为末尾添加
O_NONBLOCK //设置打开的文件描述符为非阻塞
练习
1、使用fcntl函数实现复制文件描述符
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int main() {
int fd = open("./src/010_fcntl_test.txt", O_RDWR | O_CREAT, 0755);
if (fd < 0) {
perror("open faild");
return -1;
}
int newfd = fcntl(fd, F_DUPFD, 0);
if (newfd < 0) {
perror("fcntl faild");
return -1;
}
char content[] = "测试使用fcntl函数进行文件描述符的复制\n";
write(fd, content, sizeof(content));
close(fd);
lseek(newfd, 0, SEEK_SET);
char buf[1024];
memset(buf, 0x00, sizeof(buf));
int ret = read(newfd, buf, sizeof(buf));
printf("ret=[%d],buf=[%s]\n", ret, buf);
close(newfd);
return 0;
}
2、使用fcntl函数设置在打开的文件末尾添加内容。
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int main() {
int fd = open("./src/011_fcntl_test.txt", O_RDWR | O_CREAT, 0755);
if (fd < 0) {
perror("open faild");
return -1;
}
char content[] = "测试使用fcntl函数进行在文件末尾添加内容\n";
write(fd, content, sizeof(content));
lseek(fd, 0, SEEK_SET);
int flags = fcntl(fd, F_GETFL, 0);
flags |= O_APPEND;
fcntl(fd, F_SETFL, flags);
char content1[] = "这是追加的内容\n";
write(fd, content1, sizeof(content1));
close(fd);
return 0;
}
目录操作函数
opendir
函数描述
打开一个目录
函数原型
#include <sys/types.h>
#include <dirent.h>
DIR* opendir(const char* name);
DIR* fdopendir(int fd);
函数返回值
- 指向目录的指针
函数参数
- 要遍历的目录(相对路径或者绝对路径)
readdir
函数描述
读取目录内容--目录项
函数原型
#include <dirent.h>
struct dirent* readdir(DIR* dirp);
函数返回值
- 成功:读取的目录项指针
- 失败或到结尾:NULL
函数参数
- opendir函数的返回值
struct dirent{
ino_t d_ino; // 此目录进入点的inode
off_t d_off; // 目录文件开头至此目录进入点的位移
signed short int d_reclen; // d_name 的长度,不包含NULL 字符
unsigned char d_type; // d_name 所指的文件类型
char d_name[256]; // 文件名
};
d_type
的取值:DT_BLK
- 块设备DT_CHR
- 字符设备DT_DIR
- 目录DT_LNK
- 软连接DT_FIFO
- 管道DT_REG
- 普通文件DT_SOCK
- 套接字DT_UNKNOWN
- 未知
closedir
函数描述
关闭目录
函数原型
#include <sys/types.h>
#include <dirent.h>
int closedir(DIR* dirp);
函数返回值
- 成功返回
0
,失败返回-1
函数参数
- opendir函数的返回值
读取目录内容的一般步骤
//1 打开目录
DIR * pDir = opendir(“dir”);
//2 循环读取文件
while ((p = readdir(pDir)) != NULL) {}
//3 关闭目录
closedir(pDir);
练习
1、遍历指定目录下的所有文件,并判断文件类型。
#include <dirent.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
int main() {
struct dirent *pdir;
DIR *pDIR = opendir("./src");
while ((pdir = readdir(pDIR)) != NULL) {
if (strcmp(pdir->d_name, ".") == 0 || strcmp(pdir->d_name, "..") == 0) {
continue;
}
printf("文件名:[%s]", pdir->d_name);
switch (pdir->d_type) {
case DT_BLK:
printf("\t-块设备文件\n");
break;
case DT_CHR:
printf("\t-字符设备文件\n");
break;
case DT_FIFO:
printf("\t-管道文件\n");
break;
case DT_DIR:
printf("\t-目录文件\n");
break;
case DT_LNK:
printf("\t-软链接文件\n");
break;
case DT_REG:
printf("\t-普通文件\n");
break;
case DT_SOCK:
printf("\t-套接字文件\n");
break;
case DT_UNKNOWN:
printf("\t-未知文件\n");
break;
default:
break;
}
}
closedir(pDIR);
return 0;
}
2、递归遍历目录下所有的文件, 并判断文件类型
特别注意: 递归遍历指定目录下的所有文件的时候, 要过滤掉.和..文件, 否则会进入死循环
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <dirent.h>
void printDirType(const char *path, int level);
int main(int argc, char *argv[])
{
if (argc != 2)
{
printf("run as ./main path\n");
return -1;
}
printDirType(argv[1], 0);
return 0;
}
void printDirType(const char *path, int level)
{
DIR *dir = opendir(path);
char name[256];
struct dirent *p = NULL;
while ((p = readdir(dir)) != NULL)
{
memset(name, 0x0, sizeof(name));
sprintf(name, "%s", p->d_name);
if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0)
{
continue;
}
for (int i = 0; i < level; ++i)
{
printf(" ");
}
printf("%s\t", name);
switch (p->d_type)
{
case DT_BLK:
printf("块设备\n");
break;
case DT_CHR:
printf("字符设备\n");
break;
case DT_DIR:
printf("目录\n");
// 拼接新路径并递归调用
char subName[1024] = {0};
strcpy(subName, path);
subName[strlen(subName)] = '/';
strcat(subName, name);
printDirType(subName, level + 1);
break;
case DT_LNK:
printf("软链接\n");
break;
case DT_FIFO:
printf("管道\n");
break;
case DT_REG:
printf("普通文件\n");
break;
case DT_SOCK:
printf("套接字\n");
break;
case DT_UNKNOWN:
printf("未知\n");
break;
default:
break;
}
}
return;
}
3、实现ls命令
studyC.h
存储在/usr/include/
目录中
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <sys/types.h>
// 校验程序运行参数
#define ARGS_CHECK(argc, num) \
{ \
if (argc != num) { \
fprintf(stderr, "args error\n"); \
return -1; \
} \
}
// 出错校验
#define ERROR_CHECK(ret, num, msg) \
{ \
if (ret == num) { \
perror(msg); \
return -1; \
} \
}
myLs.c
#include <studyC.h>
int main(int argc, char *argv[]) {
ARGS_CHECK(argc, 2);
DIR *dir = opendir(argv[1]);
ERROR_CHECK(dir, NULL, "opendir");
struct dirent *dirNode;
while ((dirNode = readdir(dir)) != NULL) {
if (strcmp(dirNode->d_name, ".") == 0 || strcmp(dirNode->d_name, "..") == 0) {
continue;
}
printf("%s ", dirNode->d_name);
}
puts("");
closedir(dir);
return 0;
}
4、实现ls -l命令
#include <studyC.h>
int main(int argc, char *argv[]) {
ARGS_CHECK(argc, 2);
DIR *dir = opendir(argv[1]);
ERROR_CHECK(dir, NULL, "opendir");
// 切换工作目录到输入的目录
chdir(argv[1]);
struct dirent *dirNode;
struct stat fileNode;
char fileInfo[1024] = {0};
struct tm *fileTime;
while ((dirNode = readdir(dir)) != NULL) {
/*
// 跳过.和..目录
if (strcmp(dirNode->d_name, ".") == 0 || strcmp(dirNode->d_name, "..") == 0) {
continue;
}
*/
int lstatRet = lstat(dirNode->d_name, &fileNode);
ERROR_CHECK(lstatRet, -1, "lstat");
/*
// 使用if判断文件类型
if ((fileNode.st_mode & S_IFMT) == S_IFSOCK) {
printf("s ");
} else if ((fileNode.st_mode & S_IFMT) == S_IFLNK) {
printf("l ");
} else if ((fileNode.st_mode & S_IFMT) == S_IFREG) {
printf("- ");
} else if ((fileNode.st_mode & S_IFMT) == S_IFBLK) {
printf("b ");
} else if ((fileNode.st_mode & S_IFMT) == S_IFDIR) {
printf("d ");
} else if ((fileNode.st_mode & S_IFMT) == S_IFCHR) {
printf("c ");
} else if ((fileNode.st_mode & S_IFMT) == S_IFIFO) {
printf("f ");
} else if ((fileNode.st_mode & S_IFMT) == S_IFMT) {
printf("? ");
}
*/
// 使用switch判断文件类型
switch (fileNode.st_mode & S_IFMT) {
case S_IFSOCK:
printf("s");
break;
case S_IFLNK:
printf("l");
break;
case S_IFREG:
printf("-");
break;
case S_IFBLK:
printf("b");
break;
case S_IFDIR:
printf("d");
break;
case S_IFCHR:
printf("c");
break;
case S_IFIFO:
printf("f");
break;
case S_IFMT:
printf("?");
}
// 判断拥有者读权限
if ((fileNode.st_mode & S_IRUSR) != 0) {
printf("r");
} else {
printf("-");
}
// 判断拥有者写权限
if ((fileNode.st_mode & S_IWUSR) != 0) {
printf("w");
} else {
printf("-");
}
// 判断拥有者执行权限
if ((fileNode.st_mode & S_IXUSR) != 0) {
printf("x");
} else {
printf("-");
}
// 判断文件所属组用户对文件的操作权限
if ((fileNode.st_mode & S_IRGRP) != 0) {
printf("r");
} else {
printf("-");
}
if ((fileNode.st_mode & S_IWGRP) != 0) {
printf("w");
} else {
printf("-");
}
if ((fileNode.st_mode & S_IXGRP) != 0) {
printf("x");
} else {
printf("-");
}
// 判断其他人对文件的操作权限
if ((fileNode.st_mode & S_IROTH) != 0) {
printf("r");
} else {
printf("-");
}
if ((fileNode.st_mode & S_IWOTH) != 0) {
printf("w");
} else {
printf("-");
}
if ((fileNode.st_mode & S_IXOTH) != 0) {
printf("x");
} else {
printf("-");
}
fileTime = localtime(&fileNode.st_mtime);
printf(" %ld %s %s %8ld %d月 %d %02d:%02d %s\n", fileNode.st_nlink, getpwuid(fileNode.st_uid)->pw_name, getgrgid(fileNode.st_gid)->gr_name, fileNode.st_size, fileTime->tm_mon + 1, fileTime->tm_mday, fileTime->tm_hour, fileTime->tm_min, dirNode->d_name);
}
closedir(dir);
return 0;
}
补充说明
使用stat获取文件信息的时间无法准确获取链接文件的文件类型信息,需要使用lstat