免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12345下一页
最近访问板块 发新帖
查看: 8031 | 回复: 45
打印 上一主题 下一主题

如何更巧妙的利用shell的fd [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2007-01-06 20:03 |只看该作者 |倒序浏览
建议大家先看看这个链接,此帖完全是因受它启发,甚至可说是它的续,只因两贴的讨论偏重于不同方向,所以令开一帖

http://bbs.chinaunix.net/viewthr ... p;extra=&page=1

上面一帖,r2007兄的shell版本,遗留了一个问题没有解决,就是如何让截断文件那句获得我们希望它截断文件的位置,而不是用计算文件大小来获得。下面是我的两种解法


  1. exec 3<"$1" 4<>"$1" 5<"$1"
  2. head -10 <&3;head -10 <&5
  3. dd <&3 >&4
  4. :|dd of="$1" obs=$(wc -c <&5) seek=1
  5. exec 3<&- 4>&- 5<&-
复制代码

  1. exec 3<"$1" 4<>"$1" 5<"$1"
  2. head -10 <&3
  3. dd <&3 >&4
  4. :|dd of="$1" obs=$(($(wc -c <&5)-$(wc -c <&4))) seek=1
  5. exec 3<&- 4>&- 5<&-
复制代码


两个地方可能需要解释
  1. dd <&3 >&3
复制代码

此处,由于fd3保存了head命令保存在其中,指向第11行开头的文件指针。而fd4中仍然保存着指向文件头的文件指针。
因此,此句利用了两个文件指针错位,以及fd4是以读写方式打开的,将11行后的内容写从第1行到倒数第11行,文件最后10行内容未变。执行结束,fd3保存的文件指针指向文件尾,fd4的指向倒数第10行头。
  1. wc -c <&5
复制代码
此句将获得fd5所保存的文件指针到fd5打开文件的文件尾的字节数。

这里仍然需要计算obs,是因为我在shell下实在找不到另外的办法截断文件了(尝试过向指向某位置的fd写入文件结束符或空字符,都无效~究竟还有没有别的方法呢?请大家指教)。但计算obs的方式毕竟变成了利用文件指针来获得,不再需要从外部来获得,也算一些进步吧~。
而且,更有价值的是,上面两种方法,效率进一步提高,当处理seq 10000000生成的文件时,其效率是perl方法的10倍,文件越大,效率差将越大。

上面的内容主要还是续woodie兄那帖,下面该讨论下本帖的问题了。
除了我代码中的head和错位文件指针读写,还有设置dd的操作数,另外还有其他的准确定位文件指针的方法么?
我想到了sed的q命令,如果能利用它来准确定位,那么效率和应用宽度都将非常理想。可惜
  1. exec 3<file
  2. sed '1q' <&3
  3. cat <&3
  4. exec 3<&-
复制代码
并不像我所想cat打印出除第一行外的其他行,看来q命令还做了一些不为我们所知的动作,导致sed处理过的fd,其保存的文件指针都指向了文件尾。

又想到了while read
  1. exec 3<file
  2. while read i
  3. do
  4.         ((n++))
  5.         echo $i
  6.         [ $n -eq 5 ] && break
  7. done <&3
  8. echo
  9. echo
  10. cat <&3
  11. exec 3<&-
复制代码

这种方法成功,可惜shell的while效率实在不好,也有瑕疵。

结论:
1. 在测试中发现,5<&3复制的fd,和dup系统调用一样,其文件指针是共享的,即对fd3的文件指针进行定位,fd5的文件指针也将改变。
2. head和dd用来准确定位fd的文件指针,从效率和代码复杂程度上考虑都很不错,可惜只能以具体字节或行数作为定位条件。
while read的方法可以使用更复杂的定位条件,可惜效率太差。

遗留问题:
1. shell下有否其他的方法截断文件?
2. 有否其他准确定位fd文件指针的方法?甚或可以兼顾效率和复杂的定位条件。

[ 本帖最后由 一梦如是 于 2007-1-7 10:47 编辑 ]

论坛徽章:
0
2 [报告]
发表于 2007-01-06 20:10 |只看该作者
梦帅的1000帖果然不同凡响....可惜我先水一下吧...肥水不流外人田啊...等下写心得体会^_^

论坛徽章:
7
荣誉版主
日期:2011-11-23 16:44:17子鼠
日期:2014-07-24 15:38:07狮子座
日期:2014-07-24 11:00:54巨蟹座
日期:2014-07-21 19:03:10双子座
日期:2014-05-22 12:00:09卯兔
日期:2014-05-08 19:43:17卯兔
日期:2014-08-22 13:39:09
3 [报告]
发表于 2007-01-07 12:07 |只看该作者

回复 1楼 一梦如是 的帖子

wc -c <&5
这句又扫描了一遍原文件,影响效率,值得商榷。

论坛徽章:
0
4 [报告]
发表于 2007-01-07 14:42 |只看该作者
原帖由 r2007 于 2007-1-7 12:07 发表
wc -c <&5
这句又扫描了一遍原文件,影响效率,值得商榷。


用大小为10bytes和3GB的文件分别测试了一下,使用的real_time几乎没区别

strace一下它

  1. $strace wc -c <FC-6-i386-DVD.iso 2>&1|tail -12
  2. fstat64(0, {st_mode=S_IFREG|0755, st_size=3525195776, ...}) = 0
  3. _llseek(0, 0, [0], SEEK_CUR)            = 0
  4. _llseek(0, 0, [3525195776], SEEK_END)   = 0
  5. fstat64(1, {st_mode=S_IFIFO|0600, st_size=0, ...}) = 0
  6. mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7c24000
  7. close(0)                                = 0
  8. write(1, "3525195776\n", 113525195776
  9. )            = 11
  10. close(1)                                = 0
  11. munmap(0xb7c24000, 4096)                = 0
  12. exit_group(0)                           = ?
  13. Process 6560 detached

复制代码

发现wc -c只读取了文件的前4096字节(见mmap那行调用),又与stat命令的strace信息相对照,wc -c应该只是通过fd指向的文件表项和v节点表来获得字节数(参见APUE图3-2及相关内容)
因此,文件的大小对wc -c的效率几乎没有影响(但wc -l、wc -w都会循环调用read(2),因此它们的效率是会受到文件大小影响的)

但r2007兄的提醒让我想到了head,它恐怕是要直接调用read(2)的,果不其然,strace信息证明了:无论是-c还是-n选项,都循环调用了read(2)。
所以,可能随文件大小或文件定位影响我代码效率的,除了中间那句明显的dd读写外,就是这句head了。
这样,也说明了从效率上考虑,我第二段代码比第一段代码稍优一些(因为少用了一次head)。

论坛徽章:
7
荣誉版主
日期:2011-11-23 16:44:17子鼠
日期:2014-07-24 15:38:07狮子座
日期:2014-07-24 11:00:54巨蟹座
日期:2014-07-21 19:03:10双子座
日期:2014-05-22 12:00:09卯兔
日期:2014-05-08 19:43:17卯兔
日期:2014-08-22 13:39:09
5 [报告]
发表于 2007-01-07 17:23 |只看该作者
不熟悉strace,不过从道理上wc应该比head快。wc -c不关心内容而只关心大小,head必须要实际读取内容,因为这是它的结果。

dd的输出,如:
x+y records in
m+n records out
没有文档描述其含义,而且不知道各版本是否一致,否则有可能利用这个输出算出截断的offset.

论坛徽章:
0
6 [报告]
发表于 2007-01-07 17:29 |只看该作者
r2007兄还有否其他的shell方法截断文件?

论坛徽章:
0
7 [报告]
发表于 2007-01-07 17:40 |只看该作者
怎么象天书了。。。还没用过这样的东西
还没看完,有点不懂了:
  1. head -10 <&3
  2. 。。。
  3. 此处,由于fd3保存了head命令保存在其中,指向第11行开头的文件指针
复制代码
  1. $ cat test
  2. 1
  3. 2
  4. 3
  5. 4
  6. 5
  7. 6
  8. 7
  9. 8
  10. 9
  11. 10
  12. 11
  13. 12
  14. 13
  15. 14
  16. 15
  17. 16
  18. 17
  19. 18
  20. 19
  21. 20
  22. 21
  23. 22
  24. 23
  25. 24
  26. 25
  27. 26
  28. 27
  29. 28
  30. 29
  31. 30
  32. $
  33. $ exec 7<test
  34. $ head -10 <&7
  35. 1
  36. 2
  37. 3
  38. 4
  39. 5
  40. 6
  41. 7
  42. 8
  43. 9
  44. 10
  45. $ head -10 <&7
  46. $
复制代码

第2个 head -10 <&7怎么没东西了啊

论坛徽章:
0
8 [报告]
发表于 2007-01-07 17:49 |只看该作者
您使用的是什么系统?什么版本?
看来是您系统的head与我系统的在对文件指针的行为上不同
依您提供的信息,您系统的head是定位到文件尾了。

哎,标准化是多么重要阿~

[ 本帖最后由 一梦如是 于 2007-1-7 18:01 编辑 ]

论坛徽章:
0
9 [报告]
发表于 2007-01-07 18:01 |只看该作者
原帖由 一梦如是 于 2007-1-7 17:49 发表
汗,什么系统?什么版本?

是不是如果是可用的话,第2个head 就该打印出11-20了?
我用的HP-UX,到现在还不知道怎么sed之流看版本

论坛徽章:
0
10 [报告]
发表于 2007-01-07 18:35 |只看该作者
是的,我这里是打印出11-20行的。
类UNIX的awk、sed、grep似乎都是没有版本一说的
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP