在linux上的C中,您将如何实现cp



我在谷歌上发现了超过50行的代码,这对我想要做的事情来说是完全没有必要的。

我想在C.中实现一个非常简单的cp

这样我就可以处理缓冲区大小,看看它对性能的影响。

我只想使用Linux API调用,如read()write(),但我没有运气。

我想要一个定义为一定大小的缓冲区,这样文件1中的数据就可以被读取到缓冲区中,然后写入文件2,并一直持续到文件1达到EOF。

这是我试过的,但没有任何作用

#include <stdio.h>
#include <sys/types.h>
#define BUFSIZE 1024
int main(int argc, char* argv[]){
    FILE fp1, fp2;
    char buf[1024];
    int pos;

    fp1 = open(argv[1], "r");
    fp2 = open(argv[2], "w");
    while((pos=read(fp1, &buf, 1024)) != 0)
    {
        write(fp2, &buf, 1024);
    }

    return 0;
}

它的工作方式是./mycopy file1.txt file2.txt

此代码有一个重要问题,即无论读取多少字节,都要写入1024字节。

还有:

  1. 您不检查命令行参数的数量
  2. 您不检查源文件是否存在(如果打开)
  3. 您没有检查目标文件是否打开(权限问题)
  4. 您传递数组的地址,该地址的类型与指向数组第一个元素的指针的类型不同
  5. fp1fp2的类型都是错误的。

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <fcntl.h>
    int main(int argc, char **argv)
    {
        char buffer[1024];
        int files[2];
        ssize_t count;
        /* Check for insufficient parameters */
        if (argc < 3)
            return -1;
        files[0] = open(argv[1], O_RDONLY);
        if (files[0] == -1) /* Check if file opened */
            return -1;
        files[1] = open(argv[2], O_WRONLY | O_CREAT | S_IRUSR | S_IWUSR);
        if (files[1] == -1) /* Check if file opened (permissions problems ...) */
        {
            close(files[0]);
            return -1;
        }
        while ((count = read(files[0], buffer, sizeof(buffer))) != 0)
            write(files[1], buffer, count);
        return 0;
    }
    

转到K&R"C程序设计语言"。在那里你会看到一个你想要完成的事情的例子。尝试使用不同的缓冲区大小,您最终会看到性能最高的地方。

#include <stdio.h>
int cpy(char *, char *);
int main(int argc, char *argv[])
{
    char *fn1 = argv[1];
    char *fn2 = argv[2];
    if (cpy(fn2, fn1) == -1) {
        perror("cpy");
        return 1;
    }
    reurn 0;
}
int cpy(char *fnDest, char *fnSrc)
{
    FILE *fpDest, *fpSrc;
    int c;
    if ((fpDest = fopen(fnDest, "w")) && (fpSrc = fopen(fnSrc, "r"))) {
        while ((c = getc(fpSrc)) != EOF)
            putc(fpDest);
        fclose(fpDest);
        fclose(fpSrc);
        return 0;
    }
    return -1;
}

首先,我们从命令行获得两个文件名(argv[1]argv[2])。我们不从*argv开始的原因是它包含程序名称。

然后,我们调用cpy函数,该函数将第二个命名文件的内容复制到第一个命名文件中。

cpy中,我们声明了两个文件指针:fpDest(目标文件指针)和fpSrc(源文件指针)。我们还声明了c,即将要读取的字符。它的类型是int,因为EOF不适合char

如果我们能成功打开文件(如果fopen不返回NULL),我们从fpSrc中获取字符并将其复制到fpDest上,只要我们读取的字符不是EOF。一旦我们看到EOF,我们就关闭文件指针,并返回成功指示符0。如果我们无法打开文件,则返回-1。调用方可以检查-1的返回值,如果是,则打印一条错误消息。

好问题。与另一个好问题有关:

如何使用C在Unix上复制文件?

存在两种方法来实现";最简单的";CCD_ 24的实现。一种方法使用某种类型的文件复制系统调用函数,这是最接近Unix cp命令的C函数版本。另一种方法直接或使用FILE包装器使用缓冲区和读/写系统调用函数。

仅在内核拥有的内存中进行的文件复制系统调用可能比在内核和用户拥有的内存内进行的系统调用更快,尤其是在网络文件系统设置(机器之间的复制)中。但这将需要测试(例如使用Unix命令time),并且将取决于编译和执行代码的硬件。

也有可能是没有标准Unix库的操作系统的人想要使用你的代码。然后你会想要使用缓冲区读/写版本,因为它只依赖于<stdlib.h>并且<stdio.h>(和朋友)。

<unistd.h>

下面是一个使用unix标准库<unistd.h>中的函数copy_file_range将源文件复制到(可能不存在)目标文件的示例。拷贝发生在内核空间中。

/* copy.c
 *
 * Defines function copy:
 *
 * Copy source file to destination file on the same filesystem (possibly NFS).
 * If the destination file does not exist, it is created. If the destination
 * file does exist, the old data is truncated to zero and replaced by the 
 * source data. The copy takes place in the kernel space.
 *
 * Compile with:
 *
 * gcc copy.c -o copy -Wall -g
 */
#define _GNU_SOURCE 
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <unistd.h>
/* On versions of glibc < 2.27, need to use syscall.
 * 
 * To determine glibc version used by gcc, compute an integer representing the
 * version. The strides are chosen to allow enough space for two-digit 
 * minor version and patch level.
 *
 */
#define GCC_VERSION (__GNUC__*10000 + __GNUC_MINOR__*100 + __gnuc_patchlevel__)
#if GCC_VERSION < 22700
static loff_t copy_file_range(int in, loff_t* off_in, int out, 
  loff_t* off_out, size_t s, unsigned int flags)
{
  return syscall(__NR_copy_file_range, in, off_in, out, off_out, s,
    flags);
}
#endif
/* The copy function.
 */
int copy(const char* src, const char* dst){
  int in, out;
  struct stat stat;
  loff_t s, n;
  if(0>(in = open(src, O_RDONLY))){
    perror("open(src, ...)");
    exit(EXIT_FAILURE);
  }
  if(fstat(in, &stat)){
    perror("fstat(in, ...)");
    exit(EXIT_FAILURE);
  }
  s = stat.st_size; 
  if(0>(out = open(dst, O_CREAT|O_WRONLY|O_TRUNC, 0644))){
    perror("open(dst, ...)");
    exit(EXIT_FAILURE);
  }
  do{
    if(1>(n = copy_file_range(in, NULL, out, NULL, s, 0))){
      perror("copy_file_range(...)");
      exit(EXIT_FAILURE);
    }
    s-=n;
  }while(0<s && 0<n);
  close(in);
  close(out);
  return EXIT_SUCCESS;
}
/* Test it out.
 *
 * BASH:
 *
 * gcc copy.c -o copy -Wall -g
 * echo 'Hello, world!' > src.txt
 * ./copy src.txt dst.txt
 * [ -z "$(diff src.txt dst.txt)" ]
 *
 */
int main(int argc, char* argv[argc]){
  if(argc!=3){
    printf("Usage: %s <SOURCE> <DESTINATION>", argv[0]);
    exit(EXIT_FAILURE);
  }
  copy(argv[1], argv[2]);
  return EXIT_SUCCESS;
}

它基于我的Ubuntu20.xLinux发行版的copy_file_range手册页中的示例。查看您的手册页以了解:

> man copy_file_range

然后点击jEnter,直到进入示例部分。或者键入/example进行搜索。

<stdio.h><stdlib.h>仅

下面是一个仅使用stdlib/stdio的示例。缺点是它在用户空间中使用了一个中间缓冲区。

/* copy.c
 *
 * Compile with:
 * 
 * gcc copy.c -o copy -Wall -g
 *
 * Defines function copy:
 *
 * Copy a source file to a destination file. If the destination file already
 * exists, this clobbers it. If the destination file does not exist, it is
 * created. 
 *
 * Uses a buffer in user-space, so may not perform as well as 
 * copy_file_range, which copies in kernel-space.
 *
 */
#include <stdlib.h>
#include <stdio.h>
#define BUF_SIZE 65536 //2^16
int copy(const char* in_path, const char* out_path){
  size_t n;
  FILE* in=NULL, * out=NULL;
  char* buf = calloc(BUF_SIZE, 1);
  if((in = fopen(in_path, "rb")) && (out = fopen(out_path, "wb")))
    while((n = fread(buf, 1, BUF_SIZE, in)) && fwrite(buf, 1, n, out));
  free(buf);
  if(in) fclose(in);
  if(out) fclose(out);
  return EXIT_SUCCESS;
}
/* Test it out.
 *
 * BASH:
 *
 * gcc copy.c -o copy -Wall -g
 * echo 'Hello, world!' > src.txt
 * ./copy src.txt dst.txt
 * [ -z "$(diff src.txt dst.txt)" ]
 *
 */
int main(int argc, char* argv[argc]){
  if(argc!=3){
    printf("Usage: %s <SOURCE> <DESTINATION>n", argv[0]);
    exit(EXIT_FAILURE);
  }
  return copy(argv[1], argv[2]);
}

在使用Unix-like C API的同时,另一种确保可移植性的方法是使用GNOME(例如GLib、GIO)进行开发

https://docs.gtk.org/glib/https://docs.gtk.org/gio/

最新更新