實驗?zāi)康?/h3>
掌握Linux目錄操作方法,包括打開目錄、關(guān)閉目錄、讀取目錄文件
掌握Linux文件屬性獲取方法,包括三個獲取Linux文件屬性的函數(shù)、文件屬性解析相關(guān)的宏
掌握POSIX與ANSI C文件I/O操作方法,包括打開文件、關(guān)閉文件、創(chuàng)建文件、讀寫文件、定位文件
實驗內(nèi)容
利用POSIX API(文件操作也可以使用ANSI C標(biāo)準(zhǔn)I/O庫)編程實現(xiàn)cp –r命令,
支持將源路徑(目錄)中的所有文件和子目錄,以及子目錄中的所有內(nèi)容,全部拷貝到目標(biāo)路徑(目錄)中。
實驗內(nèi)容:cp命令與命令行參數(shù)
UNIX/Linux中在shell中輸入命令名(可執(zhí)行文件名)來啟動程序,
在命令名(可執(zhí)行文件名)之后可以跟隨一系列字符串(通過空格分割),這些字符串就是命令行參數(shù)
cp [參數(shù)] <源文件路徑> <目標(biāo)文件路徑>
cp /usr/local/src/main.c /root/main.c(文件到文件復(fù)制)
cp /usr/local/src/main.c /root (文件到目錄復(fù)制)
cp –r /usr/local/src /root(遞歸復(fù)制,用于目錄到目錄的復(fù)制)
總體程序流程
任務(wù)分解1:文件到文件拷貝
利用POSIX API在Linux系統(tǒng)上編寫應(yīng)用程序,仿寫cp命令的部分功能,將源文件復(fù)制到另外一個文件或復(fù)制到另外一個目錄。
源文件路徑和目標(biāo)文件路徑通過命令行參數(shù)來指定
1、將test1.text復(fù)制成test2.txt:
[test@linux test]$ ./mycp /home/test1.txt /usr/test2.txt
2、將test1.txt復(fù)制到/tmp目錄中:
[test@linux test]$ ./mycp /home/test1.txt /tmp(目錄)
程序流程
實驗原理:應(yīng)用程序命令行參數(shù)獲取
UNIX/Linux中C語言應(yīng)用程序的啟動函數(shù)是main
操作系統(tǒng)通過C啟動例程來啟動C程序,啟動例程會從標(biāo)準(zhǔn)輸入獲取應(yīng)用程序的命令行參數(shù),并且將這些參數(shù)傳遞給main函數(shù)
main函數(shù)定義
int main(int argc, char* argv[])
形式參數(shù):
argc :整形,命令行輸入參數(shù)的個數(shù)
argv:字符串?dāng)?shù)組,以字符串形式存儲的命令行參數(shù)
演示程序
#include <stdio.h>
int main(int argc,char *argv[])
{
int i;
for(i=0;i<argc;i++)
printf("Argv %d is %s.\n",i,argv[i]);
return 0;
}
加入將上述代碼編譯為hello.o,在命令行中分別按照兩種情況執(zhí)行:
1、./hello.o
2、./hello.o aaa bbb ccc ddd eee
演示效果
Argument 0是可執(zhí)行文件名本身
Argument 1開始才是真正的命令行參數(shù),以字符串的形式傳遞(空格作為命令行參數(shù)之間的分隔)
實驗原理: 打開文件
頭文件:fcntl.h
int open( const char *pathname, int oflag, …);
該函數(shù)打開或創(chuàng)建一個文件。其中第二個參數(shù)oflag說明打開文件的選項(第三個參數(shù)是變參,僅當(dāng)創(chuàng)建新文件時才使用)
O_RDONLY::只讀打開;
O_WRONLY:只寫打開;
O_RDWR:讀、寫打開;
O_APPEND:每次寫都加到文件尾;
O_CREAT:若此文件不存在則創(chuàng)建它,此時需要第三個參數(shù)mode,該參數(shù)約定了所創(chuàng)建文件的權(quán)限,計算方法為mode&~umask
O_EXCL:如同時指定了O_CREAT,此指令會檢查文件是否存在,若不存在則建立此文件;若文件存在,此時將出錯。
O_TRUNC:如果此文件存在,并以讀寫或只寫打開,則文件長度0
返回值是打開文件的文件描述符
實驗原理:讀文件
頭文件unistd.h
ssize_t read( int filedes, void *buf, size_t nbytes);
read函數(shù)從打開的文件中讀數(shù)據(jù)
如讀取成功,返回實際讀到的字節(jié)數(shù)。一般情況下實際讀出的字節(jié)數(shù)等于要求讀取的字節(jié)數(shù),但是也有例外:讀普通文件時,在讀到要求字節(jié)數(shù)之前就到達文件尾
如已到達文件的末尾或無數(shù)據(jù)可讀,返回0(可以作為文件讀取是否完成的判斷條件?。?/p>
如果出錯,返回-1
讀操作完成后,文件的當(dāng)前位置將從讀之前的位置加上實際讀的字節(jié)數(shù)。
實驗原理:寫文件
頭文件unistd.h
ssize_t write( int filedes, const void *buf, size_t nbytes);
write函數(shù)向打開的文件中寫數(shù)據(jù)
寫入成功返回實際寫入的字節(jié)數(shù),通常與要求寫入字節(jié)數(shù)相同
寫入出錯返回-1,出錯的原因可能是磁盤滿、沒有訪問權(quán)限、或?qū)懗^文件長度限制等等
寫操作完成后,文件的當(dāng)前位置將從寫之前的位置加上實際寫的字節(jié)數(shù)。
實驗原理:關(guān)閉文件
頭文件unistd.h
int close( int filedes );
該函數(shù)關(guān)閉打開的一個文件
關(guān)閉文件后,就不能通過該文件描述符操作該文件了
實驗原理:文件定位
頭文件unistd.h
off_t lseek( int filedes, off_t offset, int whence);
進程中每打開一個文件都有一個與其相關(guān)聯(lián)的“文件當(dāng)前位置”
打開文件時,文件當(dāng)前位置默認為文件頭(0),如果指定了O_APPEND選項則文件當(dāng)前位置變?yōu)槲募玻ㄎ募L度),
lseek函數(shù)用于設(shè)置或查詢文件當(dāng)前位置
對參數(shù)的解釋與參數(shù)whence的值有關(guān):
若whence是SEEK_SET,則將該文件當(dāng)前位置設(shè)置為文件頭+offset(以字節(jié)為單位)
若whence是SEEK_CUR,則將該文件當(dāng)前位置設(shè)置為文件當(dāng)前位置+offset (以字節(jié)為單位)
若whence是SEEK_END,則將該文件當(dāng)前位置設(shè)置為文件尾+offset個字節(jié)(以字節(jié)為單位)
offset可正可負
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdbool.h>
//#define printError printf("%s\n", strerror(errno)), exit(1);
#define printError(s) perror(s), exit(1);
extern int errno;
int max(int a, int b) {return a>=b ? a : b;}
bool isDirectory(char* path)
{
struct stat ss;
stat(path, &ss);
return S_ISDIR(ss.st_mode) ? true : false;
}
int fd_src, fd_tar;//源文件描述符,目標(biāo)文件描述符
char src[500], tar[500];//源文件路徑,目標(biāo)文件路徑
char *buf;//讀取源文件內(nèi)容
char op[30];//目標(biāo)文件存在時,覆蓋還是追加
int src_path_len, tar_path_len, buf_len;//源文件路徑/目標(biāo)文件路徑/讀取的內(nèi)容長度
int src_length;//源文件內(nèi)容大小
int main(int argc, char *argv[])
{
//源文件路徑和目標(biāo)文件路徑缺失
if(argc==1) printf("ERROR:如果需要實現(xiàn)cp命令功能,請輸入源路徑和目標(biāo)路徑!\n");
//目標(biāo)文件路徑缺失
else if(argc==2) printf("ERROR:請輸入目標(biāo)路徑!\n");
else
{
strcat(src, argv[1]);
strcat(tar, argv[2]);
if(isDirectory(src))//源路徑是目錄 報錯
{
printf("ERROR:源文件不能是目錄!\n");
exit(-1);
}
if((fd_src=open(src, O_RDWR))==-1)
printError(src);//源文件不存在或打開失敗
if((src_length=lseek(fd_src, 0, SEEK_END))==-1) //獲取源文件大小
printError(src);//lseek讀取源文件大小失敗
lseek(fd_src, 0, SEEK_SET);//源文件定位到文件首,以便下面進行read
buf = (char*)malloc(sizeof(char)*src_length*10);//動態(tài)分配內(nèi)存,存儲源文件內(nèi)容
if(read(fd_src, buf, max(0, src_length-1))==-1)
printError(src);//源文件讀取內(nèi)容失敗
buf[strlen(buf)] = '\n';
if(isDirectory(tar))//目標(biāo)文件是目錄,處理出新的目標(biāo)路徑(默認復(fù)制后的文件和源文件同名)
{
src_path_len = strlen(src);
tar_path_len = strlen(tar);
int pos = 0;
for(int i = 0; i < src_path_len; i++)
if(src[i]=='/') pos=i;
if(pos==0) tar[tar_path_len++] = '/';
for(int i = pos; i < src_path_len; i++)
tar[tar_path_len++] = src[i];
}
//printf("源路徑:%s\n目標(biāo)路徑:%s\n", src, tar);
if(open(tar, O_RDWR)==-1)//目標(biāo)文件不存在,則創(chuàng)建該文件
{
if((fd_tar=open(tar, O_RDWR|O_CREAT, 0666))==-1) printError(tar);//創(chuàng)建目標(biāo)文件失敗
}
else
{
printf("目標(biāo)文件已經(jīng)存在,您要覆蓋或追加哪一個?\n");
printf("請輸入 overwrite覆蓋 or append追加?\n");
scanf("%s", op);
while(1)
{
if(op[0]=='o') //覆蓋
{
if((fd_tar=open(tar, O_RDWR|O_TRUNC))==-1) printError(tar);
break;
}
else if(op[0]=='a')//追加
{
if((fd_tar=open(tar, O_RDWR|O_APPEND))==-1) printError(tar);
break;
}
else printf("請輸入 overwrite覆蓋 or append追加?\n");
}
}
if(write(fd_tar, buf, max(0, src_length-1))==-1) printError(tar);//寫入文件失敗
printf("All done!\n");
close(fd_src);
close(fd_tar);
free(buf);
}
return 0;
}
?任務(wù)分解2:目錄的遍歷及文件屬性獲取
實驗原理-獲取當(dāng)前工作路徑
常用函數(shù):getcwd,get_current_dir_name
頭文件:unistd.h
函數(shù)定義:
char *getcwd(char *buf, size_t size)
將當(dāng)前的工作目錄絕對路徑字符串復(fù)制到參數(shù)buf 所指的緩沖區(qū),參數(shù)size 為緩沖區(qū)大小
若參數(shù)buf 為NULL,參數(shù)size 為0,則函數(shù)根據(jù)路徑字符串的長度自動分配緩沖區(qū),并將分配的路徑字符串緩沖區(qū)指針作為函數(shù)返回值(該內(nèi)存區(qū)需要手動釋放)
失敗返回NULL
char *get_current_dir_name(void)
成功返回路徑字符串緩沖區(qū)指針(該內(nèi)存區(qū)需要手動釋放),失敗返回NULL
實驗原理-打開關(guān)閉目錄
常用函數(shù):opendir,closedir
頭文件:dirent.h
函數(shù)定義:
DIR * opendir(const char * name);
打開參數(shù)name指定的目錄,并使一個目錄流與它關(guān)聯(lián)
目錄流類似于C庫函數(shù)中的文件流
失敗返回NULL
int closedir(DIR *dir);
關(guān)閉指定目錄流,釋放相關(guān)數(shù)據(jù)結(jié)構(gòu)
成功返回0;失敗返回-1
實驗原理-讀取目錄文件
常用函數(shù):readdir
頭文件:sys/types.h;dirent.h
函數(shù)定義:
struct dirent * readdir(DIR * dir);
讀取目錄流標(biāo)識的目錄文件
目錄文件是一系列目錄項的列表,每執(zhí)行一次readdir,該函數(shù)返回指向當(dāng)前讀取目錄項結(jié)構(gòu)的指針
如果到達目錄結(jié)尾或者有錯誤發(fā)生則返回NULL
范例代碼(目錄的遍歷)
實驗原理-讀取目錄文件
重要數(shù)據(jù)結(jié)構(gòu)
struct dirent { ?? ino_t d_ino;? i節(jié)點號 ?? off_t d_off;?? 在目錄文件中的偏移 ?? usigned short d_reclen; 文件名長度 ?? unsigned char d_type;?? 文件類型 ?? char d_name[256];文件名 };
實驗原理-獲取文件屬性
常用函數(shù):stat,lstat
頭文件: sys/stat.h
函數(shù)定義:
int stat(const char *path, struct stat *buf);
int lstat(const char *path, struct stat *buf);
兩個函數(shù)參數(shù)相同,功能類似
讀取path參數(shù)所指定文件的文件屬性并將其填充到buf參數(shù)所指向的結(jié)構(gòu)體中
對于符號鏈接文件,lstat返回符號鏈接文件本身的屬性,stat返回符號鏈接引用文件的文件屬性
實驗原理-文件屬性結(jié)構(gòu)體定義
重要數(shù)據(jù)結(jié)構(gòu)
struct stat {
mode_t st_mode; 文件類型與訪問權(quán)限
ino_t st_ino; i節(jié)點號
dev_t st_dev; 文件使用的設(shè)備號
dev_t st_rdev; 設(shè)備文件的設(shè)備號
nlink_t st_nlink; 文件的硬鏈接數(shù)
uid_t st_uid; 文件所有者用戶ID
gid_t st_gid; 文件所有者組ID
off_t st_size; 文件大?。ㄒ宰止?jié)為單位)
time_t st_atime; 最后一次訪問該文件的時間
time_t st_mtime; 最后一次修改該文件的時間
time_t st_ctime; 最后一次改變該文件狀態(tài)的時間
blksize_t st_blksize; 包含該文件的磁盤塊的大小
blkcnt_t st_blocks; 該文件所占的磁盤塊 數(shù)
};
例子:獲得文件text.txt的大小
#include <stdio.h>
#include <sys/stat.h>
#define FILE_N "/home/kayshi/code/Test/test.txt"
void main(void)
{
struct stat file_stat;
stat(FILE_N, &file_stat);
printf("%ld", file_stat.st_size);
}
例子:判斷是不是目錄
#include <stdio.h>
#include <time.h>
#include <sys/stat.h>
int isdirectory(char *path) {
struct stat statbuf;
if (stat(path, &statbuf) == -1)
return 0;
else
return S_ISDIR(statbuf.st_mode);
}
int main(){
int x=isdirectory("/etc");
printf("%d",x);
}
struct stat buf; // stat 的基本使用,通過宏定義判斷文件類型
int res=stat (pathname,&buf);
if (res <0)
{
printf ("error stat\n");
return -1;
}
if (S_ISDIR(buf.st_mode))
{
printf ("文件類型為d\n");
}
實驗原理-文件類型與權(quán)限位定義
重要數(shù)據(jù)結(jié)構(gòu)
mode_t? st_mode;
無符號整數(shù),其低16位定義如下文章來源:http://www.zghlxwxcb.cn/news/detail-476901.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-476901.html
實驗原理-判定文件類型宏
是否為普通文件: S_ISREG(st_mode)
#define S_IFMT 0170000
#define S_IFREG 0100000
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
是否為目錄文件 S_ISDIR(st_mode)
是否為字符設(shè)備 S_ISCHR(st_mode)
是否為塊設(shè)備 S_ISBLK(st_mode)
是否為FIFO S_ISFIFO(st_mode)
是否為套接字 S_ISSOCK(st_mode)
是否為符號連接 S_ISLINK(st_mode)
代碼示例
int main(int argc, char *argv[])
{
int i;
struct stat buf;
char *ptr;
for (i = 1; i < argc; i++) {
printf("%s: ", argv[i]);
if (lstat(argv[i], &buf) < 0) {
err_ret("lstat error");
continue;
}
if(S_ISREG(buf.st_mode)) ptr = "regular";
else if (S_ISDIR(buf.st_mode)) ptr = "directory";
else if (S_ISCHR(buf.st_mode)) ptr = "character special";
else if (S_ISBLK(buf.st_mode)) ptr = "block special";
else if (S_ISFIFO(buf.st_mode)) ptr = "fifo";
else if (S_ISLNK(buf.st_mode)) ptr = "symbolic link";
else if (S_ISSOCK(buf.st_mode)) ptr = "socket";
else ptr = "** unknown mode **";
printf("%s\n", ptr);
}
exit(0);
}
實現(xiàn)代碼:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <dirent.h>
#include <stdbool.h>
//#define printError printf("%s\n", strerror(errno)), exit(1);
#define printError(s) perror(s), exit(1);
extern int errno;
int max(int a, int b) {return a>=b ? a : b;}
bool isDirectory(char* path)
{
struct stat ss;
stat(path, &ss);
return S_ISDIR(ss.st_mode) ? true : false;
}
char src[500], tar[500];//源文件路徑,目標(biāo)文件路徑
char *buf;//讀取源文件內(nèi)容
char op[30];//目標(biāo)文件存在時,覆蓋還是追加
int src_path_len, tar_path_len, buf_len;//源文件路徑/目標(biāo)文件路徑/讀取的內(nèi)容長度
int src_length;//源文件內(nèi)容大小
int file_to_file(char *src,char *tar){
int fd_src, fd_tar;//源文件描述符,目標(biāo)文件描述符
if((fd_src=open(src, O_RDWR))==-1) printError(src);//源文件不存在或打開失敗
if((src_length=lseek(fd_src, 0, SEEK_END))==-1) printError(src);//lseek讀取源文件大小失敗
lseek(fd_src, 0, SEEK_SET);//源文件定位到文件首,以便下面進行read
buf = (char*)malloc(sizeof(char)*src_length*10);//動態(tài)分配內(nèi)存,存儲源文件內(nèi)容
if(read(fd_src, buf, max(0, src_length-1))==-1) printError(src);//源文件讀取內(nèi)容失敗
buf[strlen(buf)] = '\n';
if(isDirectory(tar))//目標(biāo)文件是目錄,處理出新的目標(biāo)路徑(默認復(fù)制后的文件和源文件同名)
{
src_path_len = strlen(src);
tar_path_len = strlen(tar);
int pos = 0;
int i = 0;
for(i = 0; i < src_path_len; i++)
if(src[i]=='/') pos=i;
if(pos==0) tar[tar_path_len++] = '/';
for(i = pos; i < src_path_len; i++)
tar[tar_path_len++] = src[i];
}
printf("source path:%s\ntarget path:%s\n", src, tar);
if(open(tar, O_RDWR)==-1)//目標(biāo)文件不存在,則創(chuàng)建該文件
{
if((fd_tar=open(tar, O_RDWR|O_CREAT, 0666))==-1) printError(tar);//創(chuàng)建目標(biāo)文件失敗
}
else
{
printf("Object file already exists, which one do you want to operate, overwrite or append?\n");
printf("Please input overwrite(o) or append(a)?\n");
scanf("%s", op);
while(1)
{
if(op[0]=='o') //覆蓋
{
if((fd_tar=open(tar, O_RDWR|O_TRUNC))==-1) printError(tar);
break;
}
else if(op[0]=='a')//追加
{
if((fd_tar=open(tar, O_RDWR|O_APPEND))==-1) printError(tar);
break;
}
else printf("Please input overwrite(o) or append(a)?\n");
}
}
if(write(fd_tar, buf, max(0, src_length-1))==-1) printError(tar);//寫入文件失敗
printf("All done!\n");
close(fd_src);
close(fd_tar);
free(buf);
}
int dir_to_dir(char *src,char *tar){
DIR *pdir1 = opendir(src);
struct dirent *pdirent;
while ((pdirent = readdir(pdir1)))
{
if (!strcmp(pdirent->d_name , ".") || !strcmp(pdirent->d_name , ".."))
{//.和..文件不操作
continue;
}
char old[512] = { 0 };
char new[512] = { 0 };
sprintf(old , "%s%s%s" , src , "/" , pdirent->d_name);
sprintf(new , "%s%s%s" , tar , "/" , pdirent->d_name);
if (pdirent->d_type == DT_DIR)
{//是目錄,接著遞歸
int ret = mkdir(new , 0777);
dir_to_dir(old,new);
}
else
{//是文件,復(fù)制
file_to_file(old,new);
}
}
closedir(pdir1);
return 0;
}
int main(int argc, char *argv[])
{
//源文件路徑和目標(biāo)文件路徑缺失
if(argc==1) printf("ERROR:If you want to implement the cp command function, please input the source and target path!\n");
//目標(biāo)文件路徑缺失
else if(argc==2) printf("ERROR:Please input the target path!\n");
else
{
strcat(src, argv[1]);
strcat(tar, argv[2]);
if(isDirectory(src))//源路徑是目錄
{
dir_to_dir(src,tar);
}else{//源路徑是文件
file_to_file(src,tar);
}
}
return 0;
}
到了這里,關(guān)于實驗6-cp –r系統(tǒng)命令的實現(xiàn)--源路徑(目錄)中的所有文件和子目錄,以及子目錄中的所有內(nèi)容,全部拷貝到目標(biāo)路徑(目錄)中--操作系統(tǒng)實驗的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!