Linux下cp功能的实现
- Linux 下cp命令的使用方法: cp filename1 filename2 //将filename1中的内容复制到filename2中.
- 学习了Linux下文件文件i/o, 让我们一起通过学习的内容来实现一个cp命令吧!
逻辑分析
- 通过调用open()函数来打开filename1文件(已经存在并有内容), 再继续open()创建并打开filename2文件.
- 通过调用read()函数读filename1文件中的内容到缓冲区中, 再调用write()函数写进filename2文件中.
- 注意: 看似思路非常明确并简洁, 过程并不是如此哦. 接下来我们看一看实现的过程, 先让我们展示一下最终代码.
最终代码
程序源码
/* 最终程序源码
* 次代码能实现大文件的拷贝
* 如果拷贝失败, 则调用unlink()函数, 删除filename2文件 */
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#define BUFSIZE 100
int main(int argc, char **argv)
{
int fd1 = -1;
int fd2 = -1;
int rv = -1;
char buf[BUFSIZE];
if( argc!=3 )
{
printf("Input format: ./mycp filename1 filename2\n");
return -1;
}
if( (fd1=open(argv[1], O_RDONLY)) <0 )
{
perror("Open file1 failure");
return -2;
}
if( (fd2=open(argv[2], O_RDWR|O_CREAT, 0666)) < 0 )
{
perror("Open file2 failure");
return -3;
}
memset(buf, 0, sizeof(buf));
lseek(fd1, 0, SEEK_SET);
while( (rv=read(fd1, buf, sizeof(buf))) > 0)
{
if( (rv=write(fd2, buf, rv)) <0 )
{
perror("Write beter into file failure");
goto cleanup;
}
}
if( rv < 0 )
{
perror("Read data from file failuer");
goto cleanup;
}
cleanup:
if( rv < 0 )
{
unlink(argv[2]);
}
close(fd1);
close(fd2);
return 0;
}
运行结果
代码版本_一代
程序源码
// mycp_1.c
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#define BUFSIZE 100
int main(int argc, char **argv)
{
int fd1 = -1;
int fd2 = -1;
int rv1 = -1;
int rv2 = -1;
char buf[BUFSIZE];
if( argc!=3 )
{
printf("Input format: ./mycp filename1 filename2\n");
return -1;
}
if( (fd1=open(argv[1], O_RDONLY)) <0 )
{
perror("Open file1 failure");
return -2;
}
if( (fd2=open(argv[2], O_RDWR|O_CREAT, 0666)) < 0 )
{
perror("Open file2 failure");
return -3;
}
memset(buf, 0, sizeof(buf));
lseek(fd1, 0, SEEK_SET);
while( (rv1=read(fd1, buf, sizeof(buf))) > 0)
{
if( (rv2=write(fd2, buf, sizeof(buf))) <0 )
{
perror("Write beter into file failure");
unlink(argv[2]);
goto cleanup;
}
}
if( rv1 < 0 )
{
perror("Read data from file failuer");
unlink(argv[2]);
goto cleanup;
}
close(fd1);
close(fd2);
cleanup:
close(fd1);
close(fd2);
return 0;
}
运行结果
在这里我们发现拷贝后的文件变大, 在代码中我们设置的缓冲区buf大小为100.
文件恰好是buf的整数倍, 也就是说我们最后一次写文件写了一个buf大小.
我们如何让最后一次只读到剩余文件的大小, 并将实际大小写进去呢.
让我们看代码_二代如何实现
代码版本_二代
程序源码
// mycp_2.c
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#define BUFSIZE 100
int main(int argc, char **argv)
{
int fd1 = -1;
int fd2 = -1;
int rv1 = -1;
int rv2 = -1;
char buf[BUFSIZE];
struct stat buf1;
static int a = 0;
if( argc!=3 )
{
printf("Input format: ./mycp filename1 filename2\n");
return -1;
}
if( (fd1=open(argv[1], O_RDONLY)) <0 )
{
perror("Open file1 failure");
return -2;
}
if( (fd2=open(argv[2], O_RDWR|O_CREAT, 0666)) < 0 )
{
perror("Open file2 failure");
return -3;
}
memset(buf, 0, sizeof(buf));
lseek(fd1, 0, SEEK_SET);
static int b = 0;
stat(argv[1], &buf1);
printf("file size: %ld\n", buf1.st_size);
a = (buf1.st_size/sizeof(buf)); //a为多少个sizeof(buf).
while( b < a+1 )
{
b++;
if( b < a+1 )
{
if( (rv1=read(fd1, buf, sizeof(buf))) <0 )
{
perror("Read data from file failuer");
goto cleanup;
}
if( (rv2=write(fd2, buf, sizeof(buf))) <0 )
{
perror("Write beter into file failure");
goto cleanup;
}
}
else if( (buf1.st_size-(a)*sizeof(buf)) != 0 )
{
if( (rv1=read(fd1, buf, buf1.st_size-(a)*sizeof(buf))) <0 )
{
perror("Read data from file failuer");
goto cleanup;
}
if( (rv2=write(fd2, buf, buf1.st_size-(a)*sizeof(buf))) <0 )
{
perror("Write beter into file failure");
goto cleanup;
}
return 0;
}
else //文件大小恰好是buf的整数倍
{
return 0;
}
}
close(fd1);
close(fd2);
cleanup:
close(fd1);
close(fd2);
return 0;
}
运行结果
在一代代码中, 最后一次总是写进去整个sizeof(buf)大小
那我们先计算文件大小, 在计算一下需要a个完整的sizeof(buf)
我们就复制a次, 每次都是sizeof(buf)大小
还有剩余, 那么我们将剩余(buf1.st_size-(a)*sizeof(buf))的写进filename2文件中即可
如果文件大小恰好和buf设置的大小为整数倍怎么办呢?
我们在代码83行中, 已经考虑到了哦! 最后读取文件数为0 也就是else 的条件哦!
我们完成了cp命令的拷贝, 学习到了获取文件大小的方法.
当然了, 这种是逻辑复杂, 并不是最优方法.
总结
特别注意使用if() 判断时, "()"中有既有加减运算和比较时, 一定要注意优先级的问题.
学到了如何获取文件大小: int stat(const char * file_name,struct stat *buf);
stat()用来将参数file_name所指的文件状态, 复制到参数buf所指的结构中.
在代码二代中, 我们的思路其实是没问题的, 但是这样太过于复杂.
我们可以在代码一代中进行改进, read()函数调用之后的返回值就是实际读到的大小.
在我们进行write()时, 第三个参数应该为 read()函数调用的返回值即可.
这样进行修改之后就变成我们最终代码.
经过这个cp命令的实现, 面对出现的问题, 产生自己的想法以及解决问题的方法, 这才是最重要的.