实验目的

掌握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系统命令的实现--源路径(目录)中的所有文件和子目录,以及子目录中的所有内容,全部拷贝到目标路径(目录)中--操作系统实验相关推荐

  1. linux实验试题 cp,51CTO博客-专业IT技术博客创作平台-技术成就梦想

    刚学习"cp"命令,不会使用,不知如何是好,到底怎样能复制正确,怎样会出错,很多疑问,还是做个实验证明一下吧! 1.在/ab下创建1.txt,在/cd下创建2.txt,在/ef下创 ...

  2. linux 复制指定目录下的全部文件到另一个目录中,linux cp 文件夹

    前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家.点击跳转到教程. linux复制指定目录下的全部文件到另一个目录中 复制指定目录下的全部文件到另一个目录中 文件及目录 ...

  3. linux cp目录到指定目录,linux复制指定目录下的全部文件到另一个目录中,linux cp 文件夹...

    解压war包    jar -xvf   xxxx.war linux复制指定目录下的全部文件到另一个目录中 复制指定目录下的全部文件到另一个目录中 文件及目录的复制是经常要用到的.linux下进行复 ...

  4. 将linux目录中的特定文件推送到手机_linux复制指定目录下的全部文件到另一个目录中,linux cp 文件夹...

    linux复制指定目录下的全部文件到另一个目录中 复制指定目录下的全部文件到另一个目录中 文件及目录的复制是经常要用到的.linux下进行复制的命令为cp. 假设复制源目录 为 dir1 ,目标目录为 ...

  5. [flex]报错,Resource Path Location Type 源路径条目“… Unknown Flex 问题

    如果你有两个这样的共享父文件夹相同的Flex项目: /用户/您的名称/文件/ - dev的路径/ ProjectOne /用户/您的名称/文件/ - dev的路径/ ProjectTwo 和Proje ...

  6. Linux源码编译(一):从头文件说起

    Linux源码编译(一):从头文件说起 2013-04-10 14:37:24 分类: LINUX 在Linux体系结构章节中,主要让大家对Linux结构层次有一定的了解,没有过多的长篇大论,力求简结 ...

  7. java 动态分区 链表_大二作业——操作系统实验——C语言用双向链表,模拟实现动态分区式存储管理...

    实验:动态分区式存储管理 实验内容: 编写程序模拟完成动态分区存储管理方式的内存分配和回收.实验具体包括:首先确定内存空闲分配表:然后采用最佳适应算法完成内存空间的分配和回收:最后编写主函数对所做工作 ...

  8. linux实验试题 cp,cp命令实验,cp命令

    cp命令实验,cp命令 创建条件 [root@localhost ~]#mkdir /source [root@localhost~]#mkdir /target [root@localhost~]# ...

  9. cp -r dir1/. dir2 表示将dir1下的文件复制到dir2,不包括dir1目录

    cp -r dir1/. dir2 表示将dir1下的文件复制到dir2,不包括dir1目录 https://baijiahao.baidu.com/s?id=1640827749992218805& ...

最新文章

  1. 北斗导航 | 复杂环境下卫星导航算法(理论)
  2. Linux内核中ioremap映射的透彻理解
  3. 台式机自动关机+自动重启问题
  4. linux故障模式,Linux操作系统出现严重故障后的救援模式
  5. Java设计模式-简单工厂模式(Static Factory Method)
  6. java web 基础框架搭建_JavaWeb之搭建自己的MVC框架(一)
  7. 使用kconfig生成autoconf.h
  8. NSIS 简易教程(四)
  9. HTML 按钮交互,20 个交互非常棒的按钮代码_html/css_WEB-ITnose
  10. python简易版成绩管理系统_Python学生成绩管理系统简洁版
  11. 我!程序媛!上家阿里巴巴,下家字节跳动!薪资翻番!选择裸辞躺平!无限期休息!...
  12. php 红包算法教程,php仿微信红包分配算法的实现方法
  13. 论文翻译——Rapid 2D-to-3D conversion——快速2D到3D转换
  14. matlab元胞数组的创建和显示
  15. Hey AI,请写一首披头士风格的歌给我
  16. firefox添加百度搜索引擎
  17. 1496_关于约翰惠勒与费曼
  18. 辽宁多部门联合整治“保健”市场乱象
  19. 【实习】一面日记 记录实习的点点滴滴
  20. 趣解 ceph rgw multisite data sync 机制

热门文章

  1. 新时期下大型数据中心机房给排水及消防设计要点分析
  2. JAVA基础数据类型转化
  3. 打破界限,获得新生——观电影《云图》有感
  4. android 11 移除桌面搜索栏
  5. mysql 数据导出导入
  6. 【PyTorch系例】torch.Tensor详解和常用操作
  7. tf.extract_image_patches以及pytorch的extract_patches
  8. linux配置网卡、修改主机名和映射文件(手把手配置)
  9. WSL安装SSH服务
  10. Acwing 823.排列