所以,我刚才在这里问过,但这个问题有一半是因为我太笨了。但我还是有问题。我希望这比以前的问题更清楚。
我正在编写POSIX cat
,我几乎可以使用它,但我有两个问题:
cat
不能从管道中读取,我真的不知道为什么(重定向(<
)工作正常)my-cat < file
将从stdin读取,直到它被终止,但它必须从stdin读取,如果没有任何文件,它必须等待终止。#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, char *argv[])
{
char opt;
while ((opt = getopt(argc, argv, "u")) != EOF) {
switch(opt) {
case 'u':
/* Make the output un-buffered */
setbuf(stdout, NULL);
break;
default:
break;
}
}
argc -= optind;
argv += optind;
int i = 0, fildes, fs = 0;
do {
/* Check for operands, if none or operand = "-". Read from stdin */
if (argc == 0 || !strcmp(argv[i], "-")) {
fildes = STDIN_FILENO;
} else {
fildes = open(argv[i], O_RDONLY);
}
/* Check for directories */
struct stat fb;
if (!fstat(fildes, &fb) && S_ISDIR(fb.st_mode)) {
fprintf(stderr, "pcat: %s: Is a directory\n", argv[i]);
i++;
continue;
}
/* Get file size */
fs = fb.st_size;
/* If bytes are read, write them to stdout */
char *buf = malloc(fs * sizeof(char));
while ((read(fildes, buf, fs)) > 0)
write(STDOUT_FILENO, buf, fs);
free(buf);
/* Close file if it's not stdin */
if (fildes != STDIN_FILENO)
close(fildes);
i++;
} while (i < argc);
return 0;
}
发布于 2021-05-12 12:18:56
管道没有尺寸,终端也没有。对于此类文件,st_size
字段的内容未定义。(在我的系统中,它似乎总是包含0,但我不认为有任何跨平台的保证。)
因此,您一次读取整个文件并再次将其全部写入的计划对于非常规文件是不可行的,甚至对它们也是有风险的(读取不能保证返回所请求的全部字节数)。如果文件很大,也会占用不必要的内存。
一个更好的策略是读入一个固定大小的缓冲区,并且只写出成功读取的字节数。重复此操作,直到到达文件结束为止,这由返回0的read()
指示.这就是你解决第二个问题的方法。
同样地,write()
也不能保证写出您要求它的全部字节数,所以您需要检查它的返回值,如果它很短,请再次尝试写出其余的字节。
下面是一个例子:
#define BUFSIZE 65536 // arbitrary choice, can be tuned for performance
ssize_t nread;
char buf[BUFSIZE]; // or char *buf = malloc(BUFSIZE);
while ((nread = read(filedes, buf, BUFSIZE)) > 0) {
ssize_t written = 0;
while (written < nread) {
ssize_t ret = write(STDOUT_FILENO, buf + written, nread - written);
if (ret <= 0)
// handle error
written += ret;
}
}
if (nread < 0)
// handle error
最后,您的程序通常缺少错误检查;例如,如果文件无法打开,它将继续执行filedes == -1
操作。检查您发出的每个系统调用的返回值并相应地处理错误是非常重要的。这对于一个程序在现实生活中的使用是非常重要的,甚至对于作为练习而创建的玩具程序来说,它也将对调试它们非常有帮助。(例如,错误检查可能会给您提供一些线索,帮助您找出该程序的错误所在。)
发布于 2021-05-14 15:04:39
您的cat
(您可以称它为my-cat
,但我更愿意称它为felix
,请允许我使用双关语)应该一直与stdio
一起使用,以获得stdio
包所做的缓冲的好处。下面是使用独占cat
包(在K&R中几乎完全相同)的简化stdio
版本,您将看到这是完全有效的,如所示(您将看到该结构与您的结构几乎完全相同,但我简化了数据副本/like K&R book/的处理,参数/yours的处理有点混乱/):
felix.c
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#define ERR(_code, _fmt, ...) do { \
fprintf(stderr,"%s: " _fmt, progname, \
##__VA_ARGS__); \
if (_code) exit(_code); \
} while (0)
char *progname = "cat";
void process(FILE *f);
int main(int argc, char **argv)
{
int opt;
while ((opt = getopt(argc, argv, "u")) != EOF) {
switch (opt) {
case 'u': setbuf(stdout, NULL); break;
}
}
/* for the case it has been renamed, calculate the basename
* of argv[0] (progname is used in the macro ERR above) */
progname = strrchr(argv[0], '/');
progname = progname
? progname + 1
: argv[0];
/* shift options */
argc -= optind;
argv += optind;
if (argc) {
int i;
for (i = 0; i < argc; i++) {
FILE *f = fopen(argv[i], "r");
if (!f) {
ERR(EXIT_FAILURE,
"%s: %s (errno = %d)\n",
argv[i], strerror(errno), errno);
}
process(f);
fclose(f);
}
} else {
process(stdin);
}
exit(EXIT_SUCCESS);
}
/* you don't need to complicate here, fgetc and putchar use buffering as you stated in main
* (no output buffering if you do the setbuf(NULL) and input buffering all the time). The buffer
* size is best to leave stdio to calculate it, as it queries the filesystem to get the best
* input/output size and create buffers this size. and the processing is simple with a loop like
* the one below. You'll get no appreciable difference between this and any other input/output.
* you can believe me, I've tested it. */
void process(FILE *f)
{
int c;
while ((c = fgetc(f)) != EOF) {
putchar(c);
}
}
正如您所看到的,没有为支持重定向做过什么特别的工作,因为重定向不是在程序中完成的,而是由调用它的程序完成的(在本例中是由shell完成的),当您启动一个程序时,您会收到三个已经打开的文件描述符。这些是shell正在使用的,或者shell在启动程序之前将其放置在0、1和2的位置。所以你的程序与重定向没有任何关系。一切都是在壳里完成的.这就是为什么您的程序重定向工作的原因,即使您没有为其工作做任何事情。如果要调用程序的输入、输出或重定向的标准错误(这不是从父进程接收到的标准输入、输出或错误),则只需进行重定向。但这不是我的猫的情况。
https://stackoverflow.com/questions/67510345
复制