目录相关操作
获取当前工作目录getcwd
getcwd
函数:获取当前工作目录的绝对路径
// manual
NAME
getcwd, getwd, get_current_dir_name - get current working directory
SYNOPSIS
#include <unistd.h>
char *getcwd(char *buf, size_t size);
RETURN VALUE
On success, return a pointer to a string containing the pathname of the current working directory.
On failure, return NULL, and errno is set to indicate the error.
getcwd
函数的惯用法
getcwd(NULL,0);
由内核申请合适的内存空间,保存当前工作目录的绝对路径
在man
手册中说明,使用这种方法时,需要调用者free
内存空间
实现
#include <func.h>
int main(int argc, char* argv[])
{
// ./getcwd
// 检查参数
if (argc != 1){
error(1, 0, "Usage: %s path\n", argv[0]);
}
//获取当前工作目录
char *cwd = getcwd(NULL, 0);
if (!cwd) {
error(1, erron, "getcwd %s", argv[1]);
}
puts(cwd);
free(cwd);
return 0;
]
改变当前的工作目录chdir
chdir
函数:改变进程当前的工作目录
// manual
NAME
chdir, fchdir - change working directory
SYNOPSIS
#include <unistd.h>
int chdir(const char *path);
RETURN VALUE
On success, zero is returned. On error, -1 is returned, and errno is set appropriately.
实现
int main(int argc, char* argv[])
{
// ./chdir
// 修改进程的当前目录
if (argc != 2) {
error(1, 0, "Usage: %s path\n", argv[0]);
}
// 获取当前目录
char *cwd = getcwd(NULL, 0);
puts(cwd);
// 修改目录权限
int err = chdir(argv[1]);
if (err){
error(1, errno, "chdir");
}
cwd = getcwd(NULL, 0);
puts(cwd);
return 0;
}
创建目录mkdir
// manual
NAME
mkdir, mkdirat - create a directory
SYNOPSIS
#include <sys/stat.h>
#include <sys/types.h>
int mkdir(const char *pathname, mode_t mode);
RETURN VALUE
return zero on success, or -1 if an error occurred (in which case, errno is set appropriately).
其中mode
表示权限
0777
: 目录的基本权限
0666
: 文件的基本权限
实现
int main(int argc, char* argv[])
{
// ./mkdir pathname [mode]
// 创建目录
if (argc != 3) {
error(1, 0, "Usage: %s pathname [mode]\n", argv[0]);
}
// 参数解析
mode_t mode;
sscanf(argv[1], "%o", mode);
// 创建目录
int err = mkdir(argv[1], mode);
if (err){
error(1, errno, "mkdir %s\n", argv[1]);
}
return 0;
}
删除空目录rmdir
// manual
NAME
rmdir - delete a directory
SYNOPSIS
#include <unistd.h>
int rmdir(const char *pathname);
RETURN VALUE
On success, zero is returned. On error, -1 is returned, and errno is set appro‐priately.
实现
int main(int argc, char* argv[])
{
// ./rmdir pathname
// 删除空目录
if (argc != 2) {
error(1, 0, "Usage: %s pathname\n", argv[0]);
}
int err = rmdir(argv[1]);
if (err) {
error(1, errno, "rmdir %s\n", argv[1]);
}
return 0;
}
读取格式化输入 sscanf
sscanf
函数可以将字符串以特定格式读入,可用于将字符串以特定格式写入到某个变量中
int sscanf(const char *str, const char *format, ...);
sscanf(argv[1], "%o", mode);
错误处理函数error
目录流相关操作
使用目录流,可以使开发人员在程序中有效访问和操作文件系统给的目录结构
目录流的基本单位是目录项
。
以下是目录流常见
的功能:
-
opendir
: 打开一个目录 -
closedir
:关闭一个目录流 -
readdir
: 读取下一个目录项
基本用不到系列:
-
telldir:返回目录流的当前位置
-
seekdir:移动到某个为止
-
rewinddir:移动到第一个
目录项
目录流是由一个个的目录项组成的,目录项的结构体如下:
struct dirent {
ino_t d_ino; // inode number
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_ino d_name d_type
目录项的d_type
有
DT_BLK This is a block device.
DT_CHR This is a character device.
DT_DIR This is a directory.
DT_FIFO This is a named pipe (FIFO).
DT_LNK This is a symbolic link.
DT_REG This is a regular file.
DT_SOCK This is a UNIX domain socket.
DT_UNKNOWN The file type could not be determined.
打开目录流opendir
// manual
NAME
opendir, fdopendir - open a directory
SYNOPSIS
#include <sys/types.h>
#include <dirent.h>
DIR *opendir(const char *name);
RETURN VALUE
The opendir() functions return a pointer to the directory stream.
On error, NULL is returned, and errno is set appropriately.
关闭目录流closedir
// manual
NAME
closedir - close a directory
SYNOPSIS
#include <sys/types.h>
#include <dirent.h>
int closedir(DIR *dirp);
RETURN VALUE
The closedir() function returns 0 on success. On error, -1 is returned, and errno is set appropriately.
读取目录项readdir
readdir
函数:用于读取一个目录项,并返回下一个目录项的dirp
,直到为NULL或发生错误
NAME
readdir - read a directory
SYNOPSIS
#include <dirent.h>
struct dirent *readdir(DIR *dirp);
DESCRIPTION
The readdir() function returns a pointer to a dirent structure representing the
next directory entry in the directory stream pointed to by dirp. It returns NULL
on reaching the end of the directory stream or if an error occurred.
RETURN VALUE
On success, readdir() returns a pointer to a dirent structure. (This structure
may be statically allocated; do not attempt to free(3) it.)
If the end of the directory stream is reached, NULL is returned and errno is not
changed. If an error occurs, NULL is returned and errno is set appropriately.
To distinguish end of stream from an error, set errno to zero before calling
readdir() and then check the value of errno if NULL is returned.
根据manual中描述,在使用readdir
函数读取 direction entry 结束时,需要判断是否发生错误而导致退出
readdir
函数的一个惯用法
errno = 0;
struct dirent *pcurr;
while ((pcurr = readdir(dirp)) != NULL) {
// 具体操作...
}
if (errno) {
error(1, errno, "readdir");
}
删除目录项
在Linux系统中,删除文件实际上是将硬链接数减1,可以使用unlink
函数
// manual
NAME
unlink - delete a name and possibly the file it refers to
SYNOPSIS
#include <unistd.h>
int unlink(const char *pathname);
RETURN VALUE
On success, zero is returned. On error, -1 is returned, and errno is set
appropriately.
目录和目录流使用场景
Linux系统中,一切皆文件。因此,目录可以看作是一种特殊的文件。它的内容是目录项的集合
如何正确使用针对目录和目录流的操作,就显得尤为重要。
目录操作:当涉及到对目录的增、删、查、改时使用。
目录流操作:遍历目录流时使用。
递归处理目录(重点)
递归处理目录时常用步骤
1. 打开目录流(opendir)
2. 遍历目录流
3. 关闭目录流
简易版的tree
实现tree类似于树的深度优先搜索。
需要注意的是,在实现青春版的tree时,需要忽略.
和..
,否则会导致死循环。
#include <func.h>
void dfs_print(const char *path, int width);
int directories, files;
int main(int argc, char* argv[])
{
// ./tree dir
if (argc != 2) {
error(1, 0, "Usage: %s dir\n", argv[1]);
}
// 1. 打印目录信息
puts(argv[1]);
// 2. 遍历目录流, 递归打印每一个目录项
dfs_print(argv[1], 4);
// 3. 打印统计信息
printf("%d directories, %d files\n", directories, files);
return 0;
}
void dfs_print(char *path, int depths) {
// 1. 打开文件流
DIR *pdir = opendir(path);
if (pdir == NULL) {
error(1, erron, "opendir directory");
}
// 2. 遍历目录流, 打印目录项
errno = 0;
struct dirent *entry;
while ((entry = readdir(pdir)) != NULL) {
char *name = entry->d_name;
// 忽略 . 和 ..
if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) {
continue;
}
// 打印目录项
for (int i = 0; i < depths; i++) {
printf(" ");
}
puts(name);
// 递归遍历目录
if (entry->d_type == DT_DIR) {
directories++;
// 拼接路径名
char subpath[256];
sprintf(subpath, "%s/%s", path, name );
dfs_print(subpath, depths + 4);
}
else {
files++;
}
} // curr == NULL
if (errno) {
error(1, errno, "readdir");
}
// 3. 关闭目录流
closedir(pdir);
}
递归复制目录
在实现递归复制目录时,不仅涉及到对目录的复制,还涉及到对文件的复制
#include <func.h>
void copyFile(const char* src, const char* dir);
void copyDir(const char* src, const char* dir);
int main(int argc, char* argv[])
{
// ./copyDir src dst
if (argc != 3){
error(1, 0, "Usage: %s src dst", argv[0]);
}
copyDir(argv[1], argv[2]);
return 0;
}
void copyFile(const char* src, const char* dst){
// 复制文件
// 1. 打开文件流
int src_fd = open(src, O_RDONLY);
if (src_fd == -1) {
error(1, errno, "open %s", src);
}
int dst_fd = open(dst, O_WRONLY | O_CREAT | O_EXCL);
if (dst_fd == -1) {
error(1, errno, "open %s", dst);
}
char buf[1024];
int bytes;
while ((bytes=read(src_fd, buf, sizeof(buf))) > 0){
write(dst_fd, buf, bytes);
}
// 3. 关闭文件流
close(src_fd);
close(dst_fd);
if (bytes < 0) {
error(1, errno, "read");
}
}
void copyDir(const char* src, const char* dst){
// 创建dst目录
int err = mkdir(dst, 0777);
if (err) {
error(1, errno, "mkdir %s", dst);
}
// 打开src目录
DIR* psrc = opendir(src);
if (!psrc) {
error(1, errno, "opendir %s", src);
}
// 遍历目录流
errno = 0;
struct dirent* pcurr;
while ((pcurr = readdir(psrc)) != NULL){
//
// 忽略.and..
const char* name = pcurr->d_name;
if (strcmp(name, ".")==0 || strcmp(name, "..") ==0) {
continue;
}
char subsrc[256];
char subdir[256];
sprintf(subsrc, "%s/%s", src, name);
sprintf(subdir, "%s/%s", dst, name);
// 如果该目录项是目录,则调用copyDir递归复制目录
if (pcurr->d_type == DT_DIR) {
copyDir(subsrc, subdir);
}
// 如果该目录项是文件,则调用copyFile复制文件
if (pcurr->d_type == DT_REG) {
copyFile(subsrc, subdir);
}
}
if (errno) {
error(1, errno, "readdir");
}
// 3. 关闭文件流
closedir(psrc);
}
递归删除目录
#include <func.h>
void deleteDir(const char *path){
// 1. 打开目录流
DIR *pdir = opendir(path);
if (!pdir) {
error(1, errno, "opendir %s", path);
}
// 2. 递归删除目录流
errno = 0;
struct dirent *curr;
while ((curr = readdir(pdir)) != NULL) {
// 忽略. and ..
char *filename = curr->d_name;
if (strcmp(filename, ".") == 0 || strcmp(filename, "..") == 0) {
continue;
}
char subpath[256];
sprintf(subpath, "%s/%s", path, filename);
if (curr->d_type == DT_DIR) {
deleteDir(subpath);
}
else {
unlink(subpath);
}
} // curr == NULL
if (errno) {
error(1, errno, "readdir");
}
// 删除当前目录
rmdir(path);
// 3. 关闭文件流
closedir(pdir);
}
int main(int argc, char* argv[])
{
// ./ deleteDir dir
if (argc != 2) {
error(1, 0, "Usage: %s dir\n", argv[0]);
}
// 删除目录
deleteDir(argv[1]);
return 0;
}
常用技巧
使用命令查看类型
以查看ino_t
结构体为例
gcc -E .c文件 | grep -nE "ino_t"
标签:int,errno,argv,char,error,操作,目录
From: https://www.cnblogs.com/notob/p/18120140