[英]Unix (in C) trying to write tail for XV6
嗨stackoverflow(ers)!
我正在使用XV6 OS(在此处找到文档)学习Unix,并且一直在尝试用C语言编写tail函数。预期的输出结果是:
我已经写了两个tail的版本,一个使用char * []实现,另一个通过写入文件然后从文件中读取(均在下面发布)我使用char * []实现tail的版本似乎更准确实际命令。 但是,在我要写入一个临时文件然后从中读取的版本中,我将获得更多行作为输出,但我不确定为什么会这样。 我的猜测是,当从一个文件读取并写入另一个文件时,'\\ n'的位置被弄乱了。 我非常感谢您帮助我们解决问题!
如果我做傻事,请不要生我的气。 我是Unix中C语言的新手,只是想学习。
使用char * []的tail.c
#include "types.h"
#include "stat.h"
#include "user.h"
#include "fcntl.h"
char buf [512];
void tail (int fd, int toSub) {
int n;
int numLines = 0;
int linesToPrint = 0;
char *buffer;
buffer = (char*) malloc (500000);
int buffSize = 0;
while ((n = read(fd, buf, sizeof(buf))) > 0) {
for (int i = 0; i<n; i++) {
buffer[buffSize] = (char)buf[i];
buffSize++;
if(buf[i] == '\n')
numLines++;
}
}
if (n < 0) {
printf (1, "tail: read error \n");
exit ();
}
if (numLines < toSub)
linesToPrint = 0;
linesToPrint = numLines - toSub;
int counter = 0;
for (int i = 0; i < buffSize; i++) {
if (counter >= linesToPrint)
printf(1,"%c",buffer[i]);
if (buffer[i] == '\n')
counter++;
}
free (buffer);
}
int main (int argc, char *argv[]) {
int toSub = 10;
int fd = -1;
if (argc <= 1) {
tail (0, toSub);
exit();
}
else if (argc > 1 && argv[1][0] == '-') {
char getToSub [10];
for (int k=1; k<strlen(argv[1]); k++) {
getToSub[k-1] = argv[1][k];
}
toSub = (atoi)(getToSub);
}
else {
if((fd = open (argv[1], toSub)) < 0) {
printf (1, "tail: cannot open %s\n", argv[1]);
exit ();
}
tail (fd, toSub);
close (fd);
}
if (argc > 2) {
for (int i=2; i<argc; i++) {
if((fd = open (argv[i], 0)) < 0) {
printf (1, "tail: cannot open %s\n", argv[i]);
exit ();
}
else {
tail (fd, toSub);
close (fd);
}
}
}
exit();
}
tail.c使用write
#include "types.h"
#include "stat.h"
#include "user.h"
#include "fcntl.h"
char buf [512];
void tail (int fd, int toSub) {
int n;
int numLines;
int linesToPrint;
int ptrDump;
ptrDump = open ("tailDump", O_CREATE | O_RDWR);
while ((n = read(fd, buf, sizeof(buf))) > 0) {
write (ptrDump, buf, sizeof(buf));
for (int i = 0; i<n; i++) {
if(buf[i] == '\n')
numLines++;
}
}
if (n < 0) {
printf (1, "tail: read error \n");
exit ();
}
if (numLines < toSub)
linesToPrint = 0;
linesToPrint = numLines - toSub;
close (ptrDump);
ptrDump = open ("tailDump", 0);
int counter = 0;
while ((n = read(ptrDump, buf, sizeof(buf))) > 0) {
for (int i = 0; i<n; i++) {
if (counter > linesToPrint)
printf(1,"%c",buf[i]);
if (buf[i] == '\n')
counter++;
}
}
close (ptrDump);
unlink("tailDump");
}
int main (int argc, char *argv[]) {
int toSub = 10;
int fd = -1;
if (argc <= 1) {
tail (0, toSub);
exit();
}
else if (argc > 1 && argv[1][0] == '-') {
char getToSub [10];
for (int k=1; k<strlen(argv[1]); k++) {
getToSub[k-1] = argv[1][k];
}
toSub = (atoi)(getToSub);
}
else {
if((fd = open (argv[1], toSub)) < 0) {
printf (1, "tail: cannot open %s\n", argv[1]);
exit ();
}
tail (fd, toSub);
close (fd);
}
if (argc > 2) {
for (int i=2; i<argc; i++) {
if((fd = open (argv[i], 0)) < 0) {
printf (1, "tail: cannot open %s\n", argv[i]);
exit ();
}
else {
tail (fd, toSub);
close (fd);
}
}
}
exit();
}
我的代码也放在了我的Github(在此处找到)以及tail_using_str.c和tail_using_file.c中
我认为您的问题在这里:
while ((n = read(fd, buf, sizeof(buf))) > 0) {
write (ptrDump, buf, sizeof(buf));
您读入n
个字节,但是在写入时,写入的是sizeof(buf)
个字节。 换句话说,您可能写入了太多字节。
也许您想要这样:
while ((n = read(fd, buf, sizeof(buf))) > 0) {
write (ptrDump, buf, n);
^
note
如果我做傻事,请不要生我的气。 我是Unix中C语言的新手,只是想学习。
因此,由于您提出的核心问题已经得到了回答 ,因此该回答不是严格必需的。 您发布的问题实际上提出了很多未明确询问的问题,我打算在这里回答。
预期的输出:...
tail -
用于给出文件的最后几行
根据谁? 不根据POSIX , 也不根据UNIX V7 , tail(1)
首次出现在这里。
(实际上, tail(1)
最初出现在PWB / UNIX中 ,但并未得到广泛使用。)
grep | tail
grep | tail
是给出包含的最后10个句子
您的意思是最后10 行 ,而不是句子。 grep
不产生句子。
(在苏联Unix中, grep
判处您 !!)
char *buffer;
buffer = (char*) malloc (500000);
此以及随后的exit
调用会造成内存泄漏。 您可能会说这是无害的,因为操作系统会在程序退出时将内存还给您,但是它很草率, Valgrind之类的工具会在此调用您。
在函数所有可能的退出点之前先使用free()
缓冲区,或者在堆栈上声明此缓冲区:
char buffer[500000]
您可能无法声明栈上那么大的缓冲区,具体取决于xv6的限制。 现代常见的堆栈大小限制是2 MiB,这是整个堆栈的最深限制,它由您最深的调用链中的所有函数使用。 这在现代系统中是可配置的,但在xv6中可能不是可配置的。
如果您被迫使用malloc()
选项,则可以在一行上执行此操作:
char *buffer = (char*) malloc (500000);
另外:
有buf
和buffer
是不好的风格。 懒。 为每个缓冲区指定一个目的驱动的名称,例如lineBuf
和accumBuf
buffSize
名称容易混淆。 目前尚不清楚它指的是哪个缓冲区,而且也不是缓冲区的大小。 将其accumBytes
之类的东西可以解决这两个问题。
您缺少了一堆在现代POSIX系统上必需的#includes
,而您在其中却无法使用。 我想看看xv6是否具有stdio.hh
, stdlib.h
, string.h
和unistd.h
,并#include
它们以实现POSIX的可移植性。 我还将查看是否可以通过sys/types.h
#include
types.h
,因为至少在macOS和其他Unix上这是必需的。 在现代系统上不需要user.h
,因此如果在xv6上实际上不需要它,请将其删除。
您的内存变体将整个文件读入RAM,然后跳过不想打印的RAM中的字节。 有点想法将显示如何既可以减小缓冲区的大小又不对输入数据进行两次传递。 (提示: accumBuf[toSub][sizeof(lineBuf)]
。如果您希望允许行数大于sizeof(lineBuf)
个字节,请随意将第二项乘以sizeof(lineBuf)
。)
if(buf[i] == '\\n') numLines++;
您可能应该检查累积缓冲区末尾的非'\\ n'字节,并为其添加另一行。 没有LF终止符的行并不是很干净,但是用户通常希望将尾随的片段视为行。
printf (1, "tail: read error \\n");
1,
噪音是什么? 您是否要指定stdout
? 这仅适用于write
,而不适用于printf
。 printf()
已经发送到stdout
。 (实际上,您必须使用fprintf()
将其发送到其他任何地方。)
由于这些仅在您遇到错误的情况下,这意味着您不必测试错误。
即使最终目标是xv6
这也是编写代码以实现POSIX可移植性的另一个原因:现代Unix系统C编译器对他们愿意接受的代码要严格得多。 过去,现代C编译器做了很多我们不得不依靠lint
这样的工具的事情。
exit()
exit(2)
带有一个参数,即退出状态代码,传统上为0表示干净退出,非零表示错误。 您的编译器让您脱身的唯一原因是,早期的C编译器并未严格按照函数的声明参数检查给定的参数列表。 实际上,xv6可能附带了K&R编译器,该编译器甚至没有函数原型来声明参数列表。 程序员应该做正确的事情而不会被警告。
linesToPrint = numLines - toSub;
这不是“打印行”,而是“ 跳过打印行”。 我花了5分钟的时间盯着代码,以克服语义上的不匹配。 编译器不在乎,但变量名不适用于编译器。 如果它们仅用于编译器,则将它们全部称为a
, b
等。
printf("%c",buffer[i]);
在这里使用putchar()
。
int counter = 0;
再次,懒惰。 算什么 ?
我只是第一个程序的一半,但这已经足够了。 希望您从中学到了一些东西。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.