問題描述 完成一個目錄複製命令mycp,包括目錄下的文件和子目錄, 運行結果如下: 思路 這道題目主要涉及文件讀寫操作和屬性修改。需要支持文件夾複製、文件複製,在Linux下還要支持軟鏈接的複製。 思路如下: 獲取待複製目錄的絕對路徑 根據絕對路徑進行dfs或者bfs搜索所有子目錄項 判斷子目錄是屬 ...
問題描述
完成一個目錄複製命令mycp,包括目錄下的文件和子目錄, 運行結果如下:
[email protected] [~/]# ls –la sem
total 56
drwxr-xr-x 3 beta beta 4096 Dec 19 02:53 ./
drwxr-xr-x 8 beta beta 4096 Nov 27 08:49 ../
-rw-r--r-- 1 beta beta 128 Nov 27 09:31 Makefile
-rwxr-xr-x 1 beta beta 5705 Nov 27 08:50 consumer*
-rw-r--r-- 1 beta beta 349 Nov 27 09:30 consumer.c
drwxr-xr-x 2 beta beta 4096 Dec 19 02:53 subdir/
[email protected] [~/]# mycp sem target
[email protected] [~/]# ls –la target
total 56
drwxr-xr-x 3 beta beta 4096 Dec 19 02:53 ./
drwxr-xr-x 8 beta beta 4096 Nov 27 08:49 ../
-rw-r--r-- 1 beta beta 128 Nov 27 09:31 Makefile
-rwxr-xr-x 1 beta beta 5705 Nov 27 08:50 consumer*
-rw-r--r-- 1 beta beta 349 Nov 27 09:30 consumer.c
drwxr-xr-x 2 beta beta 4096 Dec 19 02:53 subdir/
思路
這道題目主要涉及文件讀寫操作和屬性修改。需要支持文件夾複製、文件複製,在Linux下還要支持軟鏈接的複製。
思路如下:
- 獲取待複製目錄的絕對路徑
- 根據絕對路徑進行dfs或者bfs搜索所有子目錄項
- 判斷子目錄是屬於什麼類型:文件夾、普通文件、軟鏈接
- 分別對三種(Windows下只有文件夾和文件)進行複製操作
- 修改目標文件屬性與源文件保持一致
使用到的函數主要有:
Linux
判斷文件類型
int lstat(const char *pathname, struct stat *statbuf);
const char *pathname
需要判斷的文件的路徑struct stat *statbuf
用於保存文件屬性return int
0成功,-1失敗
判斷
文件類型 說明 判斷函數 例子 普通文件 一般意義上的文件 S_ISREG() hello.c 目錄文件 可包含其他文件或者目錄 S_ISDIR() /etc/ 字元設備文件 以無緩衝方式,提供對於設備的可變長度訪問 S_ISCHR() /dev/tty 塊設備文件 以帶緩衝方式,提供對於設備的固定長度訪問 S_ISBLK() /dev/sda 符號鏈接文件 指向另一個文件 S_ISLNK() /dev/cdrom 命名管道文件 用於進程間通信 S_ISFIFO() /dev/inictl 網路套接字文件 用於進程間的網路通信 S_ISSOCK() /dev/log 值得註意的是,需要先判斷是不是符號鏈接文件。對符號鏈接文件進行
S_ISREG()
判斷時會出現將其判斷為普通文件的情況。可能是由於他判斷的是鏈接文件所指向的文件的類型。因此需要先判斷是不是鏈接文件。
遍歷文件目錄
DIR *opendir(const char *name);
const char *name
待打開的目錄路徑return DIR
返回目錄數據結構
struct dirent *readdir(DIR *dirp);
```
DIR *dirp
待讀取的目錄return struct dirent*
返回順序讀取的該目錄下的目錄項註意,第一個目錄項是
.
, 第二個目錄項是..
複製文件
int open(const char *pathname, int flags, mode_t mode);
const char* pathname
待打開的文件路徑int flags
O_RDONLY, O_WRONLY, or O_RDWR
mode_t mode
return int
返回值是打開的文件描述符
int creat(const char *pathname, mode_t mode);
const char* pathname
待創建的文件名(可以包含路徑)mode_t mode
創建屬性return int
待創建文件描述符
ssize_t read(int fd, void *buf, size_t count);
int fd
待讀取的文件的描述符void* buf
讀取的內容存放緩衝區。儘管這裡是void*
,buf
在創建的時候應該是```char*``````- ```
size_t count
要讀取的內容的大小。如果大於fd
中的數目,則讀取相應大小內容 return ssize_t
返回實際讀取的內容的數目。失敗返回-1
ssize_t write(int fd, const void *buf, size_t count);
int fd
要寫入的文件的描述符const void *buf
要寫入的內容。size_t count
要寫入的內容大小return ssize_t
成功返回實際寫入的數目。失敗返回-1
複製文件夾
複製文件夾就是創建同名文件夾
int mkdir(const char *pathname, mode_t mode);
const char* pathname
待創建的目錄項的地址mode_t mode
創建目錄項的屬性return int
如果成功返回為0,否則返回-1
複製軟鏈接
ssize_t readlink(const char *pathname, char *buf, size_t bufsiz);
const cha* pathname
待讀取軟鏈接的路徑char* buf
軟鏈接中的內容保存到buf
中size_t bufsiz
buf
緩衝區的大小return ssize_t
返回值是軟鏈接指向路徑的長度
int symlink(const char *target, const char *linkpath);
const char* target
待創建的軟鏈接的路徑const char* linkpath
待創建的軟鏈接所指向的路徑return int
成功返回0,否則返回-1
獲取屬性
int lstat(const char *pathname, struct stat *statbuf);
const char *pathname
需要提取屬性的文件或者文件夾的路徑struct stat *statbuf
獲取到的屬性存儲緩衝區return int
成功返回0,否則-1
int chmod(const char *pathname, mode_t mode);
const char *pathname
待修改屬性的文件的路徑mode_t mode
將待修改文件修改改為該屬性return int
若成功返回0,否則返回-1
int chown(const char *pathname, uid_t owner, gid_t group);
const char* pathname
待更改的目錄路徑uid_t owner
如果是-1則不改變屬性gid_t group
如果是-1則不改變屬性
int lutimes(const char *filename, const struct timeval tv[2]);
這個命令用於修改目標文件的
access_time
modify_time
。lutimes
可以修改軟鏈接的屬性。const char *filename
待修改的文件路徑const struct timeval tv[2]
tv[0]
是access_time
,tv[1]
是modify_time
return int
如果成功返回0,否則返回-1
Windows
這裡主要列出所用到的函數,有需要的話可以詳細去查相關的函數說明:
FindFirstFile
FindNextFile
CreateFile
GetFileSize
ReadFile
WriteFile
_wmkdir
GetFileAttributes
SetFileAttributes
GetFileTime
FileTimeToSystemTime
SetFileTime
源代碼實現
Linux
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <utime.h>
#include <time.h>
#include <sys/time.h>
//文件夾複製採用dfs或者bfs進行搜索是等價的
void search_dfs(char *src_path, char *dest_path);
//複製文件
void copy_file(const char *src_file, const char *dst_file);
//複製文件夾
void copy_dir(const char *src_dir, const char *dst_dir);
//複製軟鏈接
void copy_sln(const char *src_file, const char *dst_file);
//修改文件屬性與源文件保持一致
void changeAttr(const char *src, const char *dst);
//修改路徑
void change_path(char *src, char *cat)
{
strcat(src, (char *)"/");
strcat(src, cat);
}
int main(int argc, char const *argv[])
{
if (argc < 3)
{
printf("No file or directory specified\n");
exit(-1);
}
if (argc > 3)
{
printf("Too many arguments\n");
exit(-1);
}
char src[1024], dest[1024];
char *current_dir = getcwd(NULL, 0);
struct stat state_of_entry;
lstat(argv[1], &state_of_entry);
if (S_ISDIR(state_of_entry.st_mode)) //目錄
{
if (chdir(argv[1])) //目錄錯誤
{
perror("chdir");
exit(-1);
}
strcpy(src, getcwd(NULL, 0)); //獲取源文件夾絕對路徑
chdir(current_dir);
lstat(argv[2], &state_of_entry);
if (S_ISDIR(state_of_entry.st_mode)) //目錄
{
if (chdir(argv[2])) //目錄錯誤
{
perror("chdir");
exit(-1);
}
strcpy(dest, getcwd(NULL, 0)); //獲取目標文件夾絕對路徑
chdir(current_dir);
chdir(dest);
// printf("SRC: %s\n", src);
// printf("DEST: %s\n", dest);
chdir(src);
search_dfs(src, dest);
}
else
{
printf("error. No destination directory.\n");
exit(-1);
}
}
else //文件直接複製
{
char dest[1024];
lstat(argv[2], &state_of_entry);
if (S_ISDIR(state_of_entry.st_mode)) //目錄
{
strcpy(dest, getcwd(NULL, 0)); //獲取目標文件夾絕對路徑
}
else
{
strcpy(dest, "./");
strcat(dest, argv[2]);
}
copy_file(argv[1], argv[2]);
}
return 0;
}
void search_dfs(char *src_path, char *dest_path)
{
// printf("Searching directory: %s\n", getcwd(NULL, 0));
DIR *src_dir = opendir(src_path);
DIR *dest_dir = opendir(dest_path);
struct dirent *entry = NULL;
struct stat state_of_entry;
while ((entry = readdir(src_dir)) != NULL)
{
lstat(entry->d_name, &state_of_entry);
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
continue;
// printf("entry->d_name: %s\n", entry->d_name);
if (S_ISLNK(state_of_entry.st_mode)) //符號鏈接
{
char src_file[1024];
char dest_file[1024];
strcpy(src_file, src_path);
change_path(src_file, entry->d_name);
strcpy(dest_file, dest_path);
change_path(dest_file, entry->d_name);
// printf("src file: %s\n", src_file);
// printf("dest file: %s\n", dest_file);
copy_sln(src_file, dest_file);
}
else if (S_ISREG(state_of_entry.st_mode)) //普通文件
{
char src_file[1024];
char dest_file[1024];
strcpy(src_file, src_path);
change_path(src_file, entry->d_name);
strcpy(dest_file, dest_path);
change_path(dest_file, entry->d_name);
// printf("src file: %s\n", src_file);
// printf("dest file: %s\n", dest_file);
copy_file(src_file, dest_file);
}
else if (S_ISDIR(state_of_entry.st_mode)) //目錄
{
char src[1024];
char dest[1024];
strcpy(src, src_path);
change_path(src, entry->d_name);
strcpy(dest, dest_path);
change_path(dest, entry->d_name);
// printf("src dir: %s\n", src);
// printf("dest dir: %s\n", dest);
copy_dir(src, dest);
search_dfs(src, dest);
}
}
}
void copy_file(const char *src_file, const char *dest_file)
{
int src_fd = open(src_file, O_RDONLY);
int dest_fd = creat(dest_file, O_WRONLY);
unsigned char buf[1024];
while (read(src_fd, buf, sizeof(buf)) > 0)
{
write(dest_fd, buf, sizeof(buf));
}
changeAttr(src_file, dest_file);
close(src_fd);
close(dest_fd);
}
void copy_dir(const char *src_dir, const char *dst_dir)
{
mkdir(dst_dir, 0777);
changeAttr(src_dir, dst_dir);
}
void copy_sln(const char *src_file, const char *dst_file)
{
char buf[1024];
memset(buf, 0, sizeof(buf));
int len = 0;
if ((len = readlink(src_file, buf, sizeof(buf))) > 0)
{
printf("%s\n", buf);
if (symlink(buf, dst_file) == -1)
{
perror("symlink");
}
}
changeAttr(src_file, dst_file);
}
void changeAttr(const char *src, const char *dst)
{
struct stat attr_of_src;
lstat(src, &attr_of_src);
//修改文件屬性
chmod(dst, attr_of_src.st_mode);
//修改文件用戶組
chown(dst, attr_of_src.st_uid, attr_of_src.st_gid);
//修改文件訪問、修改時間
struct timeval time_buf[2];
time_buf[1].tv_sec = attr_of_src.st_mtime;
time_buf[0].tv_sec = attr_of_src.st_atime;
if(lutimes(dst, time_buf) == -1)
{
printf("%s\n", dst);
perror("lutimes");
}
struct utimbuf tbuf;
tbuf.actime = attr_of_src.st_atime;
tbuf.modtime = attr_of_src.st_mtime;
utime(dst, &tbuf);
struct stat dst_attr_of_src;
lstat(dst, &dst_attr_of_src);
if (dst_attr_of_src.st_mtime != attr_of_src.st_mtime)
printf("%s : %d\n", dst, attr_of_src.st_mtime);
}
Windows
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <Windows.h>
#include <cstring>
#define BUF_SIZE 1024
using namespace std;
//文件夾複製採用dfs或者bfs進行搜索是等價的
void search_dfs(wchar_t* src_path, wchar_t* dest_path);
//複製文件
void copy_file(const wchar_t* src_file, const wchar_t* dst_file);
//複製文件夾
void copy_dir(const wchar_t* src_dir, const wchar_t* dst_dir);
//修改文件屬性與源文件保持一致
void change_attr(const wchar_t* src_name, const wchar_t* dst_name, HANDLE h_src, HANDLE h_dst);
int wmain(int argc, wchar_t* argv[])
{
setlocale(LC_ALL, "");
wchar_t* src_path = new wchar_t[BUF_SIZE];
wchar_t* dst_path = new wchar_t[BUF_SIZE];
ZeroMemory(src_path, BUF_SIZE);
ZeroMemory(dst_path, BUF_SIZE);
wcscpy(src_path, argv[1]);
wcscat(src_path, L"\\*");
wcscpy(dst_path, argv[2]);
wcout << L"src_path" << src_path << endl;
wcout << L"dst_path" << dst_path << endl;
//wcscpy(src_path, L"E:\\薪火培訓\\*");
//wcscpy(dst_path, L"E:\\2333");
search_dfs(src_path, dst_path);
delete[] src_path;
delete[] dst_path;
return 0;
}
//複製文件
void copy_file(const wchar_t* src_file, const wchar_t* dst_file)
{
HANDLE h_src = ::CreateFile(
src_file,
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS,
NULL);
HANDLE h_dst = ::CreateFile(
dst_file,
GENERIC_WRITE | GENERIC_READ,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (h_src == INVALID_HANDLE_VALUE || h_dst == INVALID_HANDLE_VALUE)
{
printf("Open file error!\n");
CloseHandle(h_src);
CloseHandle(h_dst);
exit(-1);
}
DWORD all_bytes = GetFileSize(h_src, NULL);
char* buffer = new char[all_bytes + 1];
ZeroMemory(buffer, all_bytes + 1);
DWORD bytes = 0;
ReadFile(h_src, buffer, all_bytes, &bytes, NULL);
WriteFile(h_dst, buffer, all_bytes, &bytes, NULL);
change_attr(src_file, dst_file, h_src, h_dst);
CloseHandle(h_src);
CloseHandle(h_dst);
delete[] buffer;
return;
}
//複製文件夾
void copy_dir(const wchar_t* src_dirr, const wchar_t* dst_dir)
{
wchar_t* src_dir = new wchar_t[BUF_SIZE];
wcscpy(src_dir, src_dirr);
src_dir[wcslen(src_dir) - 2] = '\0';
_wmkdir(dst_dir);
HANDLE h_src = CreateFile(
src_dir,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_DELETE,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS,
NULL
);
if (h_src == INVALID_HANDLE_VALUE)
{
printf("Open src directory error!\n");
CloseHandle(h_src);
exit(-1);
}
HANDLE h_dst = CreateFile(
dst_dir,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_DELETE,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS,
NULL
);
if (h_dst == INVALID_HANDLE_VALUE)
{
printf("Open dst directory error!\n");
CloseHandle(h_dst);
exit(-1);
}
change_attr(src_dir, dst_dir, h_src, h_dst);
CloseHandle(h_src);
CloseHandle(h_dst);
return;
}
void search_dfs(wchar_t* src_path, wchar_t* dst_path)
{
HANDLE h_find;
WIN32_FIND_DATA find_data;
LARGE_INTEGER size;
h_find = FindFirstFile(src_path, &find_data);
if (h_find == INVALID_HANDLE_VALUE)
{
cout << "Fail to find first file" << endl;
return;
}
copy_dir(src_path, dst_path);
do
{
wcout << find_data.cFileName << endl;
if (wcscmp(find_data.cFileName, L".") == 0 || wcscmp(find_data.cFileName, L"..") == 0)
{
continue;
}
if (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)//是目錄
{
wchar_t n_src_path[BUF_SIZE];
wcscpy(n_src_path, src_path);
n_src_path[wcslen(n_src_path) - 1] = '\0';
wcscat(n_src_path, find_data.cFileName);
wcscat(n_src_path, L"\\*");
wchar_t n_dst_path[BUF_SIZE];
wcscpy(n_dst_path, dst_path);
wcscat(n_dst_path, L"\\");
wcscat(n_dst_path, find_data.cFileName);
copy_dir(n_src_path, n_dst_path);
search_dfs(n_src_path, n_dst_path);
}
else
{
//size.LowPart = find_data.nFileSizeLow;
//size.HighPart = find_data.nFileSizeHigh;
//wcout << find_data.cFileName << "\t" << size.QuadPart << " bytes" << endl;
wchar_t n_src_path[BUF_SIZE];
wcscpy(n_src_path, src_path);
n_src_path[wcslen(n_src_path) - 1] = '\0';
wcscat(n_src_path, find_data.cFileName);
wchar_t n_dst_path[BUF_SIZE];
wcscpy(n_dst_path, dst_path);
wcscat(n_dst_path, L"\\");
wcscat(n_dst_path, find_data.cFileName);
copy_file(n_src_path, n_dst_path);
}
} while (FindNextFile(h_find, &find_data));
return;
}
//修改文件屬性與源文件保持一致
void change_attr(const wchar_t* src_name, const wchar_t* dst_name, HANDLE h_src, HANDLE h_dst)
{
DWORD attr = GetFileAttributes(src_name);
SetFileAttributes(dst_name, attr);
FILETIME t_create, t_access, t_write;
SYSTEMTIME syst_create, syst_access, syst_write;
GetFileTime(h_src, &t_create, &t_access, &t_write);
FileTimeToSystemTime(&t_create, &syst_create);
//cout << syst_create.wDay << endl;
SetFileTime(h_dst, &t_create, &t_access, &t_write);
GetFileTime(h_dst, &t_create, &t_access, &t_write);
FileTimeToSystemTime(&t_create, &syst_create);
//cout << syst_create.wDay << endl;
}