实验6-cp –r系统命令的实现--源路径(目录)中的所有文件和子目录,以及子目录中的所有内容,全部拷贝到目标路径(目录)中--操作系统实验
实验目的
掌握Linux目录操作方法,包括打开目录、关闭目录、读取目录文件
掌握Linux文件属性获取方法,包括三个获取Linux文件属性的函数、文件属性解析相关的宏
掌握POSIX与ANSI C文件I/O操作方法,包括打开文件、关闭文件、创建文件、读写文件、定位文件
实验内容
利用POSIX API(文件操作也可以使用ANSI C标准I/O库)编程实现cp –r命令,
支持将源路径(目录)中的所有文件和子目录,以及子目录中的所有内容,全部拷贝到目标路径(目录)中。
实验内容:cp命令与命令行参数
UNIX/Linux中在shell中输入命令名(可执行文件名)来启动程序,
在命令名(可执行文件名)之后可以跟随一系列字符串(通过空格分割),这些字符串就是命令行参数
cp [参数] <源文件路径> <目标文件路径>cp /usr/local/src/main.c /root/main.c(文件到文件复制)cp /usr/local/src/main.c /root (文件到目录复制)cp –r /usr/local/src /root(递归复制,用于目录到目录的复制)
总体程序流程
任务分解1:文件到文件拷贝
利用POSIX API在Linux系统上编写应用程序,仿写cp命令的部分功能,将源文件复制到另外一个文件或复制到另外一个目录。
源文件路径和目标文件路径通过命令行参数来指定
1、将test1.text复制成test2.txt:
[test@linux test]$ ./mycp /home/test1.txt /usr/test2.txt
2、将test1.txt复制到/tmp目录中:
[test@linux test]$ ./mycp /home/test1.txt /tmp(目录)
程序流程
实验原理:应用程序命令行参数获取
UNIX/Linux中C语言应用程序的启动函数是main
操作系统通过C启动例程来启动C程序,启动例程会从标准输入获取应用程序的命令行参数,并且将这些参数传递给main函数
main函数定义
int main(int argc, char* argv[])
形式参数:
argc :整形,命令行输入参数的个数
argv:字符串数组,以字符串形式存储的命令行参数
演示程序
#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,在命令行中分别按照两种情况执行:
1、./hello.o
2、./hello.o aaa bbb ccc ddd eee
演示效果
Argument 0是可执行文件名本身
Argument 1开始才是真正的命令行参数,以字符串的形式传递(空格作为命令行参数之间的分隔)
实验原理: 打开文件
头文件:fcntl.h
int open( const char *pathname, int oflag, …);
该函数打开或创建一个文件。其中第二个参数oflag说明打开文件的选项(第三个参数是变参,仅当创建新文件时才使用)
O_RDONLY::只读打开;
O_WRONLY:只写打开;
O_RDWR:读、写打开;
O_APPEND:每次写都加到文件尾;
O_CREAT:若此文件不存在则创建它,此时需要第三个参数mode,该参数约定了所创建文件的权限,计算方法为mode&~umask
O_EXCL:如同时指定了O_CREAT,此指令会检查文件是否存在,若不存在则建立此文件;若文件存在,此时将出错。
O_TRUNC:如果此文件存在,并以读写或只写打开,则文件长度0
返回值是打开文件的文件描述符
实验原理:读文件
头文件unistd.h
ssize_t read( int filedes, void *buf, size_t nbytes);
read函数从打开的文件中读数据
如读取成功,返回实际读到的字节数。一般情况下实际读出的字节数等于要求读取的字节数,但是也有例外:读普通文件时,在读到要求字节数之前就到达文件尾
如已到达文件的末尾或无数据可读,返回0(可以作为文件读取是否完成的判断条件!)
如果出错,返回-1
读操作完成后,文件的当前位置将从读之前的位置加上实际读的字节数。
实验原理:写文件
头文件unistd.h
ssize_t write( int filedes, const void *buf, size_t nbytes);
write函数向打开的文件中写数据
写入成功返回实际写入的字节数,通常与要求写入字节数相同
写入出错返回-1,出错的原因可能是磁盘满、没有访问权限、或写超过文件长度限制等等
写操作完成后,文件的当前位置将从写之前的位置加上实际写的字节数。
实验原理:关闭文件
头文件unistd.h
int close( int filedes );
该函数关闭打开的一个文件
关闭文件后,就不能通过该文件描述符操作该文件了
实验原理:文件定位
头文件unistd.h
off_t lseek( int filedes, off_t offset, int whence);
进程中每打开一个文件都有一个与其相关联的“文件当前位置”
打开文件时,文件当前位置默认为文件头(0),如果指定了O_APPEND选项则文件当前位置变为文件尾(文件长度),
lseek函数用于设置或查询文件当前位置
对参数的解释与参数whence的值有关:
若whence是SEEK_SET,则将该文件当前位置设置为文件头+offset(以字节为单位)
若whence是SEEK_CUR,则将该文件当前位置设置为文件当前位置+offset (以字节为单位)
若whence是SEEK_END,则将该文件当前位置设置为文件尾+offset个字节(以字节为单位)
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;//源文件描述符,目标文件描述符
char src[500], tar[500];//源文件路径,目标文件路径
char *buf;//读取源文件内容
char op[30];//目标文件存在时,覆盖还是追加
int src_path_len, tar_path_len, buf_len;//源文件路径/目标文件路径/读取的内容长度
int src_length;//源文件内容大小
int main(int argc, char *argv[])
{//源文件路径和目标文件路径缺失if(argc==1) printf("ERROR:如果需要实现cp命令功能,请输入源路径和目标路径!\n");//目标文件路径缺失else if(argc==2) printf("ERROR:请输入目标路径!\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);//源文件定位到文件首,以便下面进行readbuf = (char*)malloc(sizeof(char)*src_length*10);//动态分配内存,存储源文件内容if(read(fd_src, buf, max(0, src_length-1))==-1) printError(src);//源文件读取内容失败buf[strlen(buf)] = '\n';if(isDirectory(tar))//目标文件是目录,处理出新的目标路径(默认复制后的文件和源文件同名){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目标路径:%s\n", src, tar);if(open(tar, O_RDWR)==-1)//目标文件不存在,则创建该文件{if((fd_tar=open(tar, O_RDWR|O_CREAT, 0666))==-1) printError(tar);//创建目标文件失败}else {printf("目标文件已经存在,您要覆盖或追加哪一个?\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;
}
任务分解2:目录的遍历及文件属性获取
实验原理-获取当前工作路径
常用函数:getcwd,get_current_dir_name
头文件:unistd.h
函数定义:
char *getcwd(char *buf, size_t size)
将当前的工作目录绝对路径字符串复制到参数buf 所指的缓冲区,参数size 为缓冲区大小
若参数buf 为NULL,参数size 为0,则函数根据路径字符串的长度自动分配缓冲区,并将分配的路径字符串缓冲区指针作为函数返回值(该内存区需要手动释放)
失败返回NULL
char *get_current_dir_name(void)
成功返回路径字符串缓冲区指针(该内存区需要手动释放),失败返回NULL
实验原理-打开关闭目录
常用函数:opendir,closedir
头文件:dirent.h
函数定义:
DIR * opendir(const char * name);
打开参数name指定的目录,并使一个目录流与它关联
目录流类似于C库函数中的文件流
失败返回NULL
int closedir(DIR *dir);
关闭指定目录流,释放相关数据结构
成功返回0;失败返回-1
实验原理-读取目录文件
常用函数:readdir
头文件:sys/types.h;dirent.h
函数定义:
struct dirent * readdir(DIR * dir);
读取目录流标识的目录文件
目录文件是一系列目录项的列表,每执行一次readdir,该函数返回指向当前读取目录项结构的指针
如果到达目录结尾或者有错误发生则返回NULL
范例代码(目录的遍历)
实验原理-读取目录文件
重要数据结构
struct dirent { ino_t d_ino; i节点号 off_t d_off; 在目录文件中的偏移 usigned short d_reclen; 文件名长度 unsigned char d_type; 文件类型 char d_name[256];文件名 };
实验原理-获取文件属性
常用函数:stat,lstat
头文件: sys/stat.h
函数定义:
int stat(const char *path, struct stat *buf);
int lstat(const char *path, struct stat *buf);
两个函数参数相同,功能类似
读取path参数所指定文件的文件属性并将其填充到buf参数所指向的结构体中
对于符号链接文件,lstat返回符号链接文件本身的属性,stat返回符号链接引用文件的文件属性
实验原理-文件属性结构体定义
重要数据结构
struct stat { mode_t st_mode; 文件类型与访问权限 ino_t st_ino; i节点号 dev_t st_dev; 文件使用的设备号 dev_t st_rdev; 设备文件的设备号 nlink_t st_nlink; 文件的硬链接数 uid_t st_uid; 文件所有者用户ID gid_t st_gid; 文件所有者组ID off_t st_size; 文件大小(以字节为单位) time_t st_atime; 最后一次访问该文件的时间 time_t st_mtime; 最后一次修改该文件的时间 time_t st_ctime; 最后一次改变该文件状态的时间 blksize_t st_blksize; 包含该文件的磁盘块的大小 blkcnt_t st_blocks; 该文件所占的磁盘块 数 };
例子:获得文件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");}
实验原理-文件类型与权限位定义
重要数据结构
mode_t st_mode;
无符号整数,其低16位定义如下
实验原理-判定文件类型宏
是否为普通文件: 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)是否为字符设备 S_ISCHR(st_mode)是否为块设备 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);
}
实现代码:
#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];//源文件路径,目标文件路径
char *buf;//读取源文件内容
char op[30];//目标文件存在时,覆盖还是追加
int src_path_len, tar_path_len, buf_len;//源文件路径/目标文件路径/读取的内容长度
int src_length;//源文件内容大小
int file_to_file(char *src,char *tar){int fd_src, fd_tar;//源文件描述符,目标文件描述符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);//源文件定位到文件首,以便下面进行readbuf = (char*)malloc(sizeof(char)*src_length*10);//动态分配内存,存储源文件内容if(read(fd_src, buf, max(0, src_length-1))==-1) printError(src);//源文件读取内容失败buf[strlen(buf)] = '\n';if(isDirectory(tar))//目标文件是目录,处理出新的目标路径(默认复制后的文件和源文件同名){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)//目标文件不存在,则创建该文件{if((fd_tar=open(tar, O_RDWR|O_CREAT, 0666))==-1) printError(tar);//创建目标文件失败}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{//是文件,复制file_to_file(old,new);}}closedir(pdir1);return 0;
}
int main(int argc, char *argv[])
{//源文件路径和目标文件路径缺失if(argc==1) printf("ERROR:If you want to implement the cp command function, please input the source and target path!\n");//目标文件路径缺失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;
}
实验6-cp –r系统命令的实现--源路径(目录)中的所有文件和子目录,以及子目录中的所有内容,全部拷贝到目标路径(目录)中--操作系统实验相关推荐
- linux实验试题 cp,51CTO博客-专业IT技术博客创作平台-技术成就梦想
刚学习"cp"命令,不会使用,不知如何是好,到底怎样能复制正确,怎样会出错,很多疑问,还是做个实验证明一下吧! 1.在/ab下创建1.txt,在/cd下创建2.txt,在/ef下创 ...
- linux 复制指定目录下的全部文件到另一个目录中,linux cp 文件夹
前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家.点击跳转到教程. linux复制指定目录下的全部文件到另一个目录中 复制指定目录下的全部文件到另一个目录中 文件及目录 ...
- linux cp目录到指定目录,linux复制指定目录下的全部文件到另一个目录中,linux cp 文件夹...
解压war包 jar -xvf xxxx.war linux复制指定目录下的全部文件到另一个目录中 复制指定目录下的全部文件到另一个目录中 文件及目录的复制是经常要用到的.linux下进行复 ...
- 将linux目录中的特定文件推送到手机_linux复制指定目录下的全部文件到另一个目录中,linux cp 文件夹...
linux复制指定目录下的全部文件到另一个目录中 复制指定目录下的全部文件到另一个目录中 文件及目录的复制是经常要用到的.linux下进行复制的命令为cp. 假设复制源目录 为 dir1 ,目标目录为 ...
- [flex]报错,Resource Path Location Type 源路径条目“… Unknown Flex 问题
如果你有两个这样的共享父文件夹相同的Flex项目: /用户/您的名称/文件/ - dev的路径/ ProjectOne /用户/您的名称/文件/ - dev的路径/ ProjectTwo 和Proje ...
- Linux源码编译(一):从头文件说起
Linux源码编译(一):从头文件说起 2013-04-10 14:37:24 分类: LINUX 在Linux体系结构章节中,主要让大家对Linux结构层次有一定的了解,没有过多的长篇大论,力求简结 ...
- java 动态分区 链表_大二作业——操作系统实验——C语言用双向链表,模拟实现动态分区式存储管理...
实验:动态分区式存储管理 实验内容: 编写程序模拟完成动态分区存储管理方式的内存分配和回收.实验具体包括:首先确定内存空闲分配表:然后采用最佳适应算法完成内存空间的分配和回收:最后编写主函数对所做工作 ...
- linux实验试题 cp,cp命令实验,cp命令
cp命令实验,cp命令 创建条件 [root@localhost ~]#mkdir /source [root@localhost~]#mkdir /target [root@localhost~]# ...
- cp -r dir1/. dir2 表示将dir1下的文件复制到dir2,不包括dir1目录
cp -r dir1/. dir2 表示将dir1下的文件复制到dir2,不包括dir1目录 https://baijiahao.baidu.com/s?id=1640827749992218805& ...
最新文章
- 北斗导航 | 复杂环境下卫星导航算法(理论)
- Linux内核中ioremap映射的透彻理解
- 台式机自动关机+自动重启问题
- linux故障模式,Linux操作系统出现严重故障后的救援模式
- Java设计模式-简单工厂模式(Static Factory Method)
- java web 基础框架搭建_JavaWeb之搭建自己的MVC框架(一)
- 使用kconfig生成autoconf.h
- NSIS 简易教程(四)
- HTML 按钮交互,20 个交互非常棒的按钮代码_html/css_WEB-ITnose
- python简易版成绩管理系统_Python学生成绩管理系统简洁版
- 我!程序媛!上家阿里巴巴,下家字节跳动!薪资翻番!选择裸辞躺平!无限期休息!...
- php 红包算法教程,php仿微信红包分配算法的实现方法
- 论文翻译——Rapid 2D-to-3D conversion——快速2D到3D转换
- matlab元胞数组的创建和显示
- Hey AI,请写一首披头士风格的歌给我
- firefox添加百度搜索引擎
- 1496_关于约翰惠勒与费曼
- 辽宁多部门联合整治“保健”市场乱象
- 【实习】一面日记 记录实习的点点滴滴
- 趣解 ceph rgw multisite data sync 机制