kj501 回复于:2004-02-05 18:32:46 |
fp是指向打开的/var/log/ftp,fclose(fp)也只是关闭这个文件。并没有关闭stdout。所以printf()仍然可以向stdout输出信息。由于stdout已经重定向为/var/log/ftp,信息自然输出到了/var/log/ftp中。 |
flyingbxf 回复于:2004-02-16 13:09:44 |
再问一个问题:我把想要的信息重定向到文件后,便于进行分析判断下一步的操作。等将重要信息截取完后,我如果再想把输出重定向回来的话,应该用哪一个函数呢?不知道不退出程序的话,能不能重定向回来? |
lenovo 回复于:2004-02-16 13:45:35 |
你再close(fd)试试。 |
flyingbxf 回复于:2004-02-16 15:46:02 |
:em09: 看见你我就感觉有戏了。马上去测试 :mrgreen: |
flyingbxf 回复于:2004-02-16 17:06:16 |
可惜。还是不行。 |
kj501 回复于:2004-02-16 17:23:49 |
[quote:61173efb21="flyingbxf"]再问一个问题:我把想要的信息重定向到文件后,便于进行分析判断下一步的操作。等将重要信息截取完后,我如果再想把输出重定向回来的话,应该用哪一个函数呢?不知道不退出程序的话,能不能重定向回来?[/quote:61173efb21]
如果要把输出重定向回来,就应该在重定向前先保存stdout,然后在重定向完成之后,再把保存的stdout用dup2()恢复回来。 |
flyingbxf 回复于:2004-02-16 20:13:57 |
[quote:f8afc85d0f="kj501"]
如果要把输出重定向回来,就应该在重定向前先保存stdout,然后在重定向完成之后,再把保存的stdout用dup2()恢复回来。[/quote:f8afc85d0f] 怎么个保存法?压栈?还是用另外的指针指向STDOUT? |
henngy 回复于:2004-02-17 16:03:51 |
用unlink试试 |
flyingbxf 回复于:2004-02-17 16:37:06 |
unlink("/var/log/ftp");与 rm 命令 rm /var/log/ftp 都是删除文件,其具体实现有什么区别吗? |
forest077 回复于:2004-02-17 18:31:38 |
dup2我没有用过,不过我知道freopen重定向输出到文件后如何恢复到终端上,不知和这个一样不一样。 |
flyingbxf 回复于:2004-02-17 20:03:28 |
[quote:799918b9cb="forest077"]dup2我没有用过,不过我知道freopen重定向输出到文件后如何恢复到终端上,不知和这个一样不一样。[/quote:799918b9cb]
freopen怎么恢复??用fclose()吗? |
kj501 回复于:2004-02-17 21:24:40 |
[quote:18f546d406="flyingbxf"]
怎么个保存法?压栈?还是用另外的指针指向STDOUT?[/quote:18f546d406] int saved_fd ; saved_fd = STDOUT_FILENO; /* 保存标准输出 */ dup2(saved_fd, STDOUT_FILENO); /* 恢复标准输出 */ |
forest077 回复于:2004-02-17 21:44:12 |
freopen重定向后的恢复,基本原理是获得当前的tty,然后把输出流定向到当前tty即可,实现起来很简单,两三句话即可。范例如下:
//把stdout定向到文件aaa fpout=freopen("aaa","w",stdout); //现在printf会输出到文件aaa里面 fp=popen("tty","r"); fgets(str,sizeof(str),fp); str[strlen(str)-1]=0;//当前tty保存在str里面了 fclose(fp); //把fpout重定向到当前tty freopen(str,"w",fpout); //现在printf输出到当前终端 不知道这个功能适合不适合你。 |
forest077 回复于:2004-02-17 21:45:11 |
kj501的方式我要试一试,如果可以,比我的方便多了。 |
converse 回复于:2004-02-17 23:04:12 |
kj501的方法我试过了不行呀 |
win_hate 回复于:2004-02-17 23:05:09 |
[quote:3c3d0dff64="flyingbxf"]alhost bxf]#为什么关掉文件后,还能把The end写进去?运行结果如下:
[root@localhost bxf]# ./a.out [root@localhost bxf]# cat /var/log/ftp have a test The end![/quote:3c3d0dff64] 这是因为你 dup2 后, stdout 和 fp 都指向文件 /var/log/ftp, 关掉其中一个后,另一个仍然可写。 |
win_hate 回复于:2004-02-17 23:08:32 |
[quote:e653c7e9b3="kj501"]
int saved_fd ; saved_fd = STDOUT_FILENO; /* 保存标准输出 */ dup2(saved_fd, STDOUT_FILENO); /* 恢复标准输出 */[/quote:e653c7e9b3] 你写错了吧?这几行程序一般相当于: int saved_fd; save_fd = 2; dup2 (2, 2); 这怎么行? |
win_hate 回复于:2004-02-17 23:12:18 |
这个估计行:
[code:1:f3e6cd9f61] int sfd; sfd = dup (STDOUT_FILENO); /* save */ .... dup2 (sfd, STDOUT_FILENO); /* restore */ [/code:1:f3e6cd9f61] |
converse 回复于:2004-02-18 17:45:09 |
win_hate,你好!
如果我这么写: int main(void) { FILE *fp; int fd, id; id = dup(STDOUT_FILENO); fp = fopen("install.log", "w"); if (fp == NULL) { printf("read error.\n"); return 0; } fd = fileno(fp); dup2(fd, STDOUT_FILENO); printf("test\n"); fclose(fp); printf("hello\n"); dup2(id, STDOUT_FILENO); printf("world\n"); return 0; } 将在终端打印出 test hello world 如果我要做到在打开的文件install.log中写入 test hello 而在终端输出 world 应该怎么作呢? 另外,在学习UNIX编程时我看的是APUE,感觉光看书中的例子还不够呀,那些例子功能比较单一,大多为了讲述一组函数的功能的,有没有那本书将大部分函数功能都用上,实现一个比较大的程序呢 |
lenovo 回复于:2004-02-18 17:53:30 |
你的要求很奇怪哦。
那样的话你用fprintf()吧。 也没这么麻烦了。 |
win_hate 回复于:2004-02-18 18:29:20 |
[quote:1e12026a55="converse"] dup2(id, STDOUT_FILENO);
printf("world\n"); return 0; [/quote:1e12026a55] 在 dup2 (id, STDOUT_FILENO); 之前加入 [code:1:1e12026a55] fflush (stdout); [/code:1:1e12026a55] 就能达到你要求的效果了。该死的缓冲..... 对另一个问题, 我认为只有写过 ''真正的程序'' 后才能融会贯通。如果没有参加实际项目的机会,可以多看看代码。 |
converse 回复于:2004-02-18 21:55:22 |
我明白了,fflush函数把原来的test和hello强行写入打开的文件中,然后由于stdut被重新定位到屏幕上所以就在屏幕上输出world了,对吧??
我现在比较的郁闷,虽然找了一个作LINUX的公司,不过作的东西好象和我想像的有分别,暂时还用不上APUE里的知识,缺少了在实际中的锻炼学起来自然慢的多,我看了一下APUE后面有几个实际的例子,不过好象是数据库的,然后是打印机的,我这些都没有学过呀,不知道看过这本书的朋友感觉怎么样呢? 个人觉得APUE对于初学者来说太难了,很多基础的概念一带而过,还是lenovo推荐过的《UNIX程序设计教程》不错,把这两本书结合在一起看学的很快,就是可惜的是没有实践的锻炼,郁闷呀。。。。。。。。。 |
lenovo 回复于:2004-02-18 22:35:39 |
[quote:3d57c6a105="converse"]我明白了,fflush函数把原来的test和hello强行写入打开的文件中,然后由于stdut被重新定位到屏幕上所以就在屏幕上输出world了,对吧??
我现在比较的郁闷,虽然找了一个作LINUX的公司,不过作的东西好象和我想像..........[/quote:3d57c6a105] 我看APUE看的也很慢,现在看到进程高级通信那里了, 时间也不是很充足。不过感觉用处还是很大的, 很多不明白的地方,那本书都说了。 |
forest077 回复于:2004-02-19 09:45:19 |
说来惭愧,虽然我一直向别人大力推荐APUE,但是自己从来没有从头到尾看过一遍。我把它当作一本开发手册,用到时再去翻翻,呵呵。 |
flyingbxf 回复于:2004-02-19 11:01:36 |
[quote="win_hate"]
在 dup2 (id, STDOUT_FILENO); 之前加入 代码: [color=blue:5d83aef38c]fflush (stdout); [/color:5d83aef38c] quote] 我没有加fflush (stdout); 也没有出现问题啊!为什么呢?我是在linux下运行的。 |
win_hate 回复于:2004-02-19 11:41:07 |
[quote:06b22e07e5="flyingbxf"]
quote] 我没有加fflush (stdout); 也没有出现问题啊!为什么呢?我是在linux下运行的。[/quote:06b22e07e5] 我记得你是做嵌入式开发的,可能是环境不同。你换个台式机试一试可能就有这个问题。 |
huahua0459 回复于:2004-02-19 18:32:59 |
dup2首先close标准输出,然后把标准输出的文件描述符1传到fd所代表的结构中?
close(fd)其实是close了一个没有用的数(3),fclose关闭的也是这个数字(3),可以通过fileno看到。所以还可以写道这个文件中。如果关闭了close(1),则不行了。 看来FILE和文件描述符最好不一起使用。不然一定要当心啊。 |
kj501 回复于:2004-02-19 20:20:35 |
[quote:97d14ba4a1="win_hate"]
你写错了吧?这几行程序一般相当于: int saved_fd; save_fd = 2; dup2 (2, 2); 这怎么行?[/quote:97d14ba4a1] 说错了,stdout应该是1,2是stderr。 |
kj501 回复于:2004-02-19 20:42:05 |
[quote:dd9390adc3="kj501"]
int saved_fd ; saved_fd = STDOUT_FILENO; /* 保存标准输出 */ dup2(saved_fd, STDOUT_FILENO); /* 恢复标准输出 */[/quote:dd9390adc3] 我来检讨一下,由于我对dup2的使用在概念有理解错误,我给出的例子是行不通的。 经过试验,我发现dup2(int oldfd, int newfd)其中的newfd必须是一个实际打开文件的文件描述符,不能只是一个不指向任何文件的整数。 这是我作试验的代码: [code:1:dd9390adc3] #include<unistd.h> #include<stdlib.h> #include<fcntl.h> #include<sys/types.h> #include<sys/stat.h> int main() { int sfd,testfd; testfd = open("temp",O_CREAT | O_RDWR | O_APPEND); if (-1 == testfd) { printf("open file error.\n"); exit(1); } /* 先复制一个真实的文件描述符 */ sfd = dup(testfd); /* 保存标准输出 */ if (-1 == dup2(STDOUT_FILENO,sfd) ) { printf("can't save fd \n"); exit(1); } /* 重定向 */ if (-1 == dup2(testfd,STDOUT_FILENO) ) { printf("can't redirect fd error\n"); exit(1); } /* 此时向stdout写入应该输出到文件 */ write(STDOUT_FILENO,"file\n",5); /* 恢复stdout */ if (-1 != dup2(sfd,STDOUT_FILENO) ) { printf("recover fd ok \n"); /* 恢复后,写入stdout应该向屏幕输出 */ write(STDOUT_FILENO,"stdout\n",7); } } [/code:1:dd9390adc3] 如果把其中的这一行注释掉: [code:1:dd9390adc3] sfd = dup(testfd); [/code:1:dd9390adc3] dup2函数执行时肯定会失败。 但从代码中也可以看出,我前面提出的先保存文件描述符,然后在重定向之后再恢复回来的思路是行得能的。 有不当之处,还请大家多多指教! |
win_hate 回复于:2004-02-19 20:48:40 |
[quote:2138f9c2b3="kj501"]
说错了,stdout应该是1,2是stderr。[/quote:2138f9c2b3] 说得对,我写错了, 应该是1 |
win_hate 回复于:2004-02-19 21:16:51 |
[quote:b9495ae57a="kj501"]dup2函数执行时肯定会失败。
但从代码中也可以看出,我前面提出的先保存文件描述符,然后在重定向之后再恢复回来的思路是行得能的。 有不当之处,还请大家多多指教![/quote:b9495ae57a] 你对 dup2 的理解仍然有误, dup2 (a, b) 把 a 复制到 指定的“数字”(文件描述)上, 如果 b 是已打开的文件描述符, 先把 b 关掉(取消关联)。 你可以试一试, 把 [code:1:b9495ae57a] sfd = dup(testfd); [/code:1:b9495ae57a] 注释掉,然后,很重要的一点,把 sfd 初始化为一个整数, 不要太大。我估计程序不会出错。 你试过后,请把结果告诉我。谢谢。 |
flyingbxf 回复于:2004-02-20 09:20:16 |
[quote:300998a250="win_hate"]
我记得你是做嵌入式开发的,可能是环境不同。你换个台式机试一试可能就有这个问题。[/quote:300998a250] 我写错了,我的使用环境是uclinux,我在台式机上redhat8.0的环境下试了,不加fflush(stdout)果然全都输出到屏幕上了,文件里面没有写进去.不过我不明白能不能写进文件关缓冲什么事 呢? |
converse 回复于:2004-02-20 14:36:36 |
我来谈谈我的看法吧,我对这个问题还有一些疑问的
[code:1:d5193e31b0] #include <stdio.h> #include <unistd.h> int main(void) { FILE *fp; int fd, id; id = dup(STDOUT_FILENO);//这时id和STDOUT_FILENO一起指向stdout if ((fp = fopen("install.log", "w")) == NULL) { perror("read error.\n"); exit(-1); } fd = fileno(fp);//得到fp的文件描述符 dup2(fd, STDOUT_FILENO);//STDOUT_FILENO和stdout流断开, //STDOUT_FILENO指向fd的对应文件,这样使输出到达fd的文件 //fd对应的是已经打开的文件install.log,//而stdout在这里指的是终端,现在任何的输入都输入到 //install.log中 printf("hello\n"); fflush(stdout);//刷新stdout上的缓冲区,这样前面的hello写入文件//install.log中 dup2(id, STDOUT_FILENO); printf("world\n"); fclose(fp); return 0; } [/code:1:d5193e31b0] 以上的注释基本表达了我对这几个函数运用的了解,不过这里对fflush(stdout)的运用还有一点疑问,fflush(stream)刷新的是流stream上的缓冲,是不是可以理解为不论文件描述符STDOUT_FILENO定位到了哪个文件,stdout都保存有未写入文件的缓冲区的内容呢?按理来说,文件描述符比流更加底层才对呀 |
converse 回复于:2004-02-20 14:39:21 |
倒,我的代码怎么在这里变得这么乱呀 |
win_hate 回复于:2004-02-20 22:32:39 |
[quote:3ecf7dbf24="converse"]
fflush(stdout);//刷新stdout上的缓冲区,这样前面的hello写入文件 install.log中 dup2(id, STDOUT_FILENO); printf("world\n"); fclose(fp); [/quote:3ecf7dbf24] [quote:3ecf7dbf24="converse"] 不过这里对fflush(stdout)的运用还有一点疑问,fflush(stream)刷新的是流 stream上的缓冲,是不是可以理解为不论文件描述符STDOUT_FILENO定位到了哪个文件,stdout都保存有未写入文件的缓冲区的内容呢?按理来说,文件描述符比流更加底层才对呀[/quote:3ecf7dbf24] 呵呵, 确实如此。 事实上,文件指针,比如说fp, 是根据fileno(fp)来对应文件的。你看上面的代码,STDOUT_FILENO 已经对应到 install.log,所以,当fflush(stdout)后, 缓冲内容写入文件 install.log。如果不fflush,执行 dup2 (id, STDOUT_FILENO), 这时 STDOUT_FILENO又对应回屏幕了, 然后缓冲中的内容和后来的数据都输到屏幕上了。 |
kj501 回复于:2004-02-21 10:56:37 |
[quote:4624443cae="win_hate"]
注释掉,然后,很重要的一点,把 sfd 初始化为一个整数, 不要太大。我估计程序不会出错。 你试过后,请把结果告诉我。谢谢。[/quote:4624443cae] 你的说法一点不错,我将 [code:1:4624443cae] sfd = dup(testfd); [/code:1:4624443cae] 注释掉后,再将sfd初始化为0~1023之间的任何数,都可以执行成功。看来问题出在这一句: [code:1:4624443cae] dup2(STDOUT_FILENO,sfd); [/code:1:4624443cae] 我用的系统是linux(内核2.4.21),由于linux内核限制最大打开的文件总数为1024(这一点可能改进了,原来很多书上说的是最大只能打开256个文件),因此,dup2在执行时肯定会进行文件描述符的合法性检查,如果大于1023,肯定导致失败。sfd没有初始化时的值是由系统任意给出的(将sfd没有初始化的值打印出来,结果是1073817472),已经超出了1023的限制,从而导致dup2()执行失败。这才是问题的真正原因。其实在man文档中对错误原因说得很清楚:“oldfd isn't an open file descriptor, or newfd is out of the allowed range for file descriptors.” 只是我自己没有认真看。 经过这次讨论,感觉自己对dup2()的理解加深不少,APUE上对dup2()的介绍对于全面理解这两个函数的使用并不充分。以后man文档一定要仔细看。 最后,非常感谢win_hate,希望以后能和你多多交流。:) |
converse 回复于:2004-02-21 11:22:35 |
这样针对一组函数功能的讨论真的可以提高很快,我也受益匪浅呀
建议斑竹加为精华 |
win_hate 回复于:2004-02-21 12:50:17 |
[quote:a687efc748="kj501"]最后,非常感谢win_hate,希望以后能和你多多交流。[/quote:a687efc748]
kj501 兄客气了, 这里我常来,期待与大家交流,向各位学习。 |
forest077 回复于:2004-02-21 16:02:53 |
我有一个不解的地方,就是freopen实现的功能也是把一个流重定向到另一个流去,它和dup、dup2的底层实现有什么不一样吗? |
win_hate 回复于:2004-02-21 23:05:43 |
[quote:4f427b3b10="forest077"]我有一个不解的地方,就是freopen实现的功能也是把一个流重定向到另一个流去,它和dup、dup2的底层实现有什么不一样吗?[/quote:4f427b3b10]
freopen 并不是把一个流定向到另一个流,而是把一个流对应到一个新的文件: fd1 = dup (fd); fd1, fd 对应同一个文件, 但不能指定 fd1 的值。 dup2 (fd, fd1); fd1, fd 对应同一个文件,但可以指定 fd1的值,根据 kj501提供的资料, fd1 不能大于 1024 freopen ("xxx", "w", fp); 关掉 fp 对应的文件, 并打开文件 xxx, 指针 fp 的值不变。 在前两种情况中, 都有两个文件描述符出现, 但在第三种情况中, 只有一个文件指针。 使用 freopen (..., fp) 时, 原来 fp 对应的缓存写入, 关掉 fileno(fp), 寻找一个最小未用的文件描述符, 在其上打开新文件。也就是说, fp 值不变, 其指向的 FILE 结构仍然使用, 但 fileno (fp) 则有可能改变。 |
Wangwen 回复于:2004-02-21 23:42:35 |
[quote:0f345bc08a="win_hate"]
呵呵, 确实如此。 事实上,文件指针,比如说fp, 是根据fileno(fp)来对应文件的。你看上面的代码,STDOUT_FILENO 已经对应到 install.log,所以,当fflush(stdout)后, 缓冲内容写入文件 install.log。如果不fflus..........[/quote:0f345bc08a] 可以理解为 dup2首先close标准输出,然后把标准输出的文件描述符STDOUT_FILENO 传到fd所代表的fp文件指针结构中 STDOUT_FILENO 指向install.log的文件表 文件指针stdout的文件描述符为STDOUT_FILENO 所以fflush(stdout)后, 缓冲内容写入文件 install.log |
win_hate 回复于:2004-02-22 08:48:06 |
[quote:3af0064132="Wangwen"]
可以理解为 dup2首先close标准输出,然后把标准输出的文件描述符STDOUT_FILENO 传到fd所代表的fp文件指针结构中 STDOUT_FILENO 指向install.log的文件表 文件指针stdout的文件描述符为STDOUT_FILENO 所以f..........[/quote:3af0064132] 说法有误,dup2 是低级 I/O 操作函数, 不会直接与 fp 指向的文件结构打交道。 |
flyingbxf 回复于:2004-02-23 09:24:54 |
[quote:3716d38f75="converse"]
fflush(stream)刷新的是流stream上的缓冲,是不是可以理解为不论文件描述符STDOUT_FILENO定位到了哪个文件,stdout都保存有未写入文件的缓冲区的内容呢?........[/quote:3716d38f75] 不用fflush(stream)就写不进文件,但为什么不用fflush(stream)就能输出到屏幕上呢? |
flyingbxf 回复于:2004-02-23 09:29:40 |
还有就是 dup2(STDOUT_FILENO,sfd); 和
dup2(sfd,STDOUT_FILENO); 起的作用是一样的?都是将两个文件描述符指向同一个文件是吗? |
Wangwen 回复于:2004-02-23 12:08:53 |
可以理解为 dup2首先close标准输出,
将STDOUT_FILENO 指向install.log的文件表 文件指针stdout的文件描述符为STDOUT_FILENO 所以fflush(stdout)后, 缓冲内容写入文件 install.log 使用fprintf(fp,"hello\n");fflush(fp);仍然可以将hello写入install.log dup2(id, STDOUT_FILENO)使STDOUT_FILENO指向标准输出 printf("world\n");写入终端 |
Wangwen 回复于:2004-02-23 12:11:24 |
[quote:8fb1668b95="flyingbxf"]
不用fflush(stream)就写不进文件,但为什么不用fflush(stream)就能输出到屏幕上呢?[/quote:8fb1668b95] 程序结束是 执行了fflush(stream) |
win_hate 回复于:2004-02-23 14:14:13 |
[quote:40572341d5="Wangwen"]可以理解为 dup2首先close标准输出,
将STDOUT_FILENO 指向install.log的文件表 文件指针stdout的文件描述符为STDOUT_FILENO 所以fflush(stdout)后, 缓冲内容写入文件 install.log 使用fprintf(fp,"hello\n");fflush(fp);仍然可以将hello写入install.log dup2(id, STDOUT_FILENO)使STDOUT_FILENO指向标准输出 printf("world\n");写入终端[/quote:40572341d5] Great! |
Wangwen 回复于:2004-02-23 14:25:49 |
xiexie |
converse 回复于:2004-02-23 20:10:51 |
茅塞顿开,不错不错,强烈建议加精 |