完成一個目錄複製命令mycp,包括目錄下的文件和子目錄, 運行結果以下:linux
beta@bugs.com [~/]# 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/ beta@bugs.com [~/]# mycp sem target beta@bugs.com [~/]# 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下還要支持軟連接的複製。ios
思路以下:windows
使用到的函數主要有:bash
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);
ui
const char *name
待打開的目錄路徑return DIR
返回目錄數據結構struct dirent *readdir(DIR *dirp);
spa
```DIR *dirp
待讀取的目錄code
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
返回實際讀取的內容的數目。失敗返回-1ssize_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,不然返回-1ssize_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,不然返回-1int lstat(const char *pathname, struct stat *statbuf);
const char *pathname
須要提取屬性的文件或者文件夾的路徑struct stat *statbuf
獲取到的屬性存儲緩衝區return int
成功返回0,不然-1int chmod(const char *pathname, mode_t mode);
const char *pathname
待修改屬性的文件的路徑mode_t mode
將待修改文件修改改成該屬性return int
若成功返回0,不然返回-1int 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這裏主要列出所用到的函數,有須要的話能夠詳細去查相關的函數說明:
FindFirstFile
FindNextFile
CreateFile
GetFileSize
ReadFile
WriteFile
_wmkdir
GetFileAttributes
SetFileAttributes
GetFileTime
FileTimeToSystemTime
SetFileTime
#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); }
#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; }