tee 命令和 tee.c 之间的区别

Difference between tee command and tee.c

提问人:Chih-Hao Liu 提问时间:10/22/2023 最后编辑:Chih-Hao Liu 更新时间:10/22/2023 访问量:93

问:

我目前正在 Ubuntu 20.04 Linux 上使用 C 实现“tee”命令。但是,我注意到输出略有不同。

我首先用

ls -l | tee test.txt

这是输出。

enter image description here

注意:这些文件来自 Linux 编程接口的在线源代码:https://man7.org/tlpi/code/online/all_files_by_chapter.html

现在,我想使用以下存储库中的代码实现“tee”命令:https://github.com/rmascarenhas/lpi/blob/master/chap04/tee.c

#define _XOPEN_SOURCE /* getopt function */
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>

#ifndef BUF_SIZ
#define BUF_SIZ 1024
#endif

#ifndef MAX_OUT_FILES
#define MAX_OUT_FILES 128
#endif

typedef enum { FALSE, TRUE } Bool;
void helpAndLeave(const char *progname, int status);
void failure(const char *fCall);
int
main(int argc, char *argv[]) {
  int opt;
  Bool append = FALSE;
  int fd, flags;
  int fds[MAX_OUT_FILES];
  mode_t mode;
  char buf[BUF_SIZ + 1];
  ssize_t numRead;
  int i, numFiles = 0;
  /* Command line arguments parsing */
  opterr = 0;
  while ((opt = getopt(argc, argv, "+a")) != -1) {
    switch(opt) {
      case '?': helpAndLeave(argv[0], EXIT_FAILURE); break;
      case 'h': helpAndLeave(argv[0], EXIT_SUCCESS); break;
      case 'a': append = TRUE;                       break;
    }
  }
  if (optind >= argc) {
    helpAndLeave(argv[0], EXIT_FAILURE);
  }
  /* stdin redirection */
  flags = O_CREAT | O_WRONLY;
  mode = S_IRUSR | S_IWUSR; /* rw------- */
  if (append) {
    flags |= O_APPEND;
  } else {
    flags |= O_TRUNC;
  }
  for (i = optind; i < argc; ++i) {
    fds[i - optind] = fd = open(argv[i], flags, mode);
    if (fd == -1) {
      failure("open");
    }
    ++numFiles;
  }
  while ((numRead = read(STDIN_FILENO, buf, BUF_SIZ)) > 0) {
    if (write(STDOUT_FILENO, buf, numRead) != numRead) {
      failure("write");
    }
    for (i = 0; i < numFiles; ++i) {
      if (write(fds[i], buf, numRead) != numRead) {
        failure("write");
      }
    }
  }
  if (numRead == -1) {
    failure("read");
  }
  for (i = 0; i < numFiles; ++i) {
    if (close(fds[i]) == -1) {
      failure("close");
    }
  }
  return 0;
}
void
helpAndLeave(const char *progname, int status) {
  fprintf(stderr, "Usage: %s [-a] <file1> <file2> ... <fileN>\n", progname);
  exit(status);
}
void
failure(const char *fCall) {
  perror(fCall);
  exit(EXIT_FAILURE);
}

编译程序后,我用自己的 tee 程序实现

ls -l | ./tee test.txt

输出如下:enter image description here

结果是不同的。创建“test.txt”文件并将其打印到大小为 0 字节的终端。我还参考了其他存储库并对“tee.c”程序进行了修改,但结果保持不变 - “test.txt”也出现在终端中。

因此,我想询问为什么“tee.c”程序与“tee”命令相比会产生不同的输出。

谢谢你的帮助。

C Linux 系统

评论

4赞 Jonathan Leffler 10/22/2023
这可能是一个时间问题。如果命令(不是 ʼinstruction')在读取目录之前创建其输出文件,则会出现文件名。目录越大,显示文件名的输出的可能性就越大。teels
2赞 Shawn 10/22/2023
如果你使用的是 Linux,你的可能是 GNU coreutils 版本;在 github.com/coreutils/coreutils/blob/master/src/tee.c 查看其来源tee(1)
3赞 n. m. could be an AI 10/22/2023
在命令完成工作之前,您正在查看命令(任一版本)的结果。结果将不一致。像这样检查:。teels -l | tee test.txt ; ls -l test.txt
1赞 Shawn 10/22/2023
您是否在运行不同版本之间删除了 test.txt 文件?tee
0赞 Chih-Hao Liu 10/22/2023
@JonathanLeffler我会尝试在较小的目录上进行测试。谢谢你的帮助!

答:

1赞 Nick 10/22/2023 #1

其他文件和包含在输出中的原因是:teetest.txt

  1. tee是编译 tee.c 时生成的可执行文件。当您使用命令时,它会使用文件 ,因此不会出现。tee/usr/bin/tee
  2. 通常,实用程序(例如)会在其中创建文件,然后将它们移动到目的地,作为终止前的最后操作。这就是为什么在使用命令时不出现它的原因。在 中,文件将基于当前目录创建,因此,如果命令参数是 just,则将在读取目录内容之前在此处创建该文件。tee/usr/tmpteetee.ctest.txt

评论

0赞 Jonathan Leffler 10/22/2023
你能为你写信的索赔提供任何理由吗?tee/usr/tmp
0赞 Nick 10/23/2023
@JonathanLeffler,我不能给你一本书的参考,但IIRC有一本书(大约1984年)为Unix(当时的)实用程序设定了标准。其中之一是使用 tmpfile(3) 创建输出文件,然后使用 link(2) 作为完成时的最后一个操作,将其“移动”到正确的位置,并使用 unlink(2) 删除临时目录中的条目。我认为 OP 问题的证据表明这种情况仍在继续,但我还没有查看 tee 的来源。
0赞 Jonathan Leffler 10/23/2023
我很想了解更多信息。尝试从另一个挂载的文件系统将不起作用。如今,对于单体文件系统,它不太可能成为问题,但在 1984 年,这将是一个相当大的问题。因此,代码必须在与输出文件相同的文件系统上创建临时文件,最简单的方法是在与文件相同的目录中创建临时文件。你可能会想到的一本书是 Brian W Kernighan, Rob Pike The Unix Programming Environment Nov 1983。但它没有这个规则。link()/usr/tmp