20130712

如无必要,勿增实体:在很多移动硬盘中找到某个文件

本文简要介绍通过 管道使用 find,bzip2,grep,案例是把诸多硬盘的目录(和文件)结构压缩存储,用于离线查找硬盘上的文件;还涉及到流在通过管道时压缩和解压缩。
最近拍的照片,哈尔滨探望大毅,跟ZHUMAO出去玩,在这里 [http://www.douban.com/photos/album/106351444/]。


1. 问题

事实上,我没有"很多"移动硬盘,而是只有"一些"。我倒是有很多光盘,装在半米高的柜子里几乎塞满,里面都是文件,有的是软件,有的是我的笔记和资料。光盘全都用记号笔写了名字,每张光盘还都配了一张纸,打印整页的树状目录。当年盗版软件光盘能做的工作我都做了,就是为了什么时候需要某个文件的时候能一下子查出来。

但是,失败了。

文件太多,一页纸经常打不下,如果简略一些,日后就可能找不到;光盘太多,一张张找来也真是费劲。现在,移动硬盘又出现了类似的问题。当我查找某个东西的时候,经常需要把六七个移动硬盘挨个插上计算机,一顿搜索。有时候还可能忘了最佳的关键词,再重插重搜。

当年的光盘是这样解决的。用TotalCommander
(绝对一大利器,向用windows的同学强烈推荐)做每一张光盘的目录树压缩包,把这些压缩包放在机器的硬盘里。需要找某个文件的时候,搜索这个压缩包,而不用插光盘。找到文件在哪张光盘里,再按卷标找光盘。当然,这要求给文件起个有区分度的好名字。目录树压缩包,是TotalCommander的一个插件,就像ZIP或RAR一样压缩目录,只不过它的内容不是文件,只是文件名。

移动硬盘多了,也出现了当年光盘的问题。不过当年的方案不能用了,因为我现在的坐机是Linux,没用TotalCommander可用。不过,好在上述功能用简单的脚本也足够能完成,涉及到的命令也只有三个而已。

2. 索引和压缩

这一步,把移动硬盘插上,把目录树列出来,做成个压缩包。

比如,我的这块移动硬盘 mount 在 /media/Elements/ 之下,形成的目录树压缩
包的名字是Elements.dir.zip,命令如下:

$ find /media/Elements/ | bzip2 -c > Elements.dir.zip &&
timidity~/tools/tomato/unlock.mid

(1) "find /media/Elements/" 的作用是把硬盘的目录树列出来,因为后面是管道,所以没有任何输出。如果我们截获它的输出,应该类似于:
/media/Elements/
/media/Elements/
/media/Elements/files
/media/Elements/files/ted
/media/Elements/files/TOP250部电影
/media/Elements/files/video
/media/Elements/files/[沙丘].Dune_CN_01_11.mp3
/media/Elements/files/[沙丘].Dune_CN_01_12.mp3
/media/Elements/files/[沙丘].Dune_CN_01_13.mp3
/media/Elements/files/[沙丘].Dune_CN_01_14.mp3
/media/Elements/files/[沙丘].Dune_CN_01_15_A.mp3
/media/Elements/files/[沙丘].Dune_CN_01_15_B.mp3
/media/Elements/files/[沙丘].Dune_CN_01_16_A.mp3
/media/Elements/files/[沙丘].Dune_CN_01_16_B.mp3
/media/Elements/files/[沙丘].Dune_CN_01_21_A.mp3
/media/Elements/files/[沙丘].Dune_CN_01_21_B.mp3

这里之所以不用tree的原因是,tree会形成下面的效果,因而无法用grep查找到某个文件在哪个目录下。

| |-- Card
| | `-- Characters and Viewpoint Elements of Fiction Writing (33)
| | |-- Characters and Viewpoint Elements of Fiction Writing - Card.mobi
| | |-- Characters and Viewpoint Elements of Fiction Writing - Card.pdf
| | |-- cover.jpg
| | `-- metadata.opf

如上,用grep的时候会查到:
| | |-- cover.jpg

我们只知道cover.jpg在第三级,却不容易找到它所在的目录是 "(某个目录)/Card/Characters and Viewpoint
Elements of Fiction Writing (33)"。而根据后文我们会知道,用grep查找而不用眼睛一行行去看非常重要。

(2) "bzip2 -c > Elements.dir.zip"
的作用是把标准输入来的东西压缩为zip格式,命名为Elements.dir.zip。"bzip2
-c"的意思是把压缩的结果输出到标准输出,所以我们用">"把它重定向了。

在本命令中,标准输入就是 (1) 里面的 find 的输出,那个目录树结构。

(3) 后面的 "&& timidity~/tools/tomato/unlock.mid" 的用途是压缩完成以后让计算机叫一声,通知我。

压缩目录树的速度非常快,但是从一整块移动硬盘里获取目录树有时花的时间比较长,而一直盯着屏幕等程序退出绝非我辈之所为--尽可能把任务交给计算机,只要你能说明白了。

"&&"符合不同于";"之处是,"&&"要求之前的命令执行结果正确,无错误返回值,然后再叫,";"不管前面结果如何。

输完上述命令以后,我们就可以听歌看碟看书喝咖啡,等计算机通知我们这块硬盘压完了。然后换一块移动硬盘,改mount点,改压缩包的名字,回画,再去享受生活。

生活里令我们非常享受的一点是,我们没有把目录树先写成个文件,然后再压缩这个文件。我们使用了管道,而不是文件作为中介。这让我想起,不止一次,研究生同学在做项目的时候告诉我,不可能把摄像头啊数据啊什么的直接读出来,必须先写到硬盘上,或者这么做比较方便。我们不能那么做的原因,首要的原因是是这样的效率要比直接读低很多,因为硬盘比内存慢好几个数量级,另一个更重要的原因是:我们学计算机的,代码早有一天要被别人看,咱丢不起这个人呐。

如无必要,勿增实体。

3. 解压和查找

当哪天我们要查硬盘里有什么数据的时候,我们就这样:

$ bzip2 -cd *.dir.zip | grep -i justice
/media/Elements/emule/incoming/[公平:怎么做才正确?].Justice.What's.The.Right.Thing.To.Do.Episode.01.mp4
/media/Elements/emule/incoming/[公平:怎么做才正确?].Justice.What's.The.Right.Thing.To.Do.Episode.02.mp4
/media/Elements/emule/incoming/[公平:怎么做才正确?].Justice.What's.The.Right.Thing.To.Do.Episode.03.mp4
/media/Elements/emule/incoming/[公平:怎么做才正确?].Justice.What's.The.Right.Thing.To.Do.Episode.04.mp4
/media/Elements/emule/incoming/[公平:怎么做才正确?].Justice.What's.The.Right.Thing.To.Do.Episode.05.mp4
/media/Elements/emule/incoming/[公平:怎么做才正确?].Justice.What's.The.Right.Thing.To.Do.Episode.06.mp4
/media/Elements/emule/incoming/[公平:怎么做才正确?].Justice.What's.The.Right.Thing.To.Do.Episode.07.mp4
/media/Elements/emule/incoming/[公平:怎么做才正确?].Justice.What's.The.Right.Thing.To.Do.Episode.08.mp4
/media/Elements/emule/incoming/[公平:怎么做才正确?].Justice.What's.The.Right.Thing.To.Do.Episode.09.mp4
/media/Elements/emule/incoming/[公平:怎么做才正确?].Justice.What's.The.Right.Thing.To.Do.Episode.10.mp4
/media/Elements/emule/incoming/[公平:怎么做才正确?].Justice.What's.The.Right.Thing.To.Do.Episode.11.mp4
/media/Elements/emule/incoming/[公平:怎么做才正确?].Justice.What's.The.Right.Thing.To.Do.Episode.12.mp4
/media/Elements/emule/incoming/公平与正义.第06集.Justice.What's.The.Right.Thing.To.Do.EP06.HDTV.HALFCD-TLF.mkv
/media/Elements/emule/incoming/公平与正义.第07集.Justice.What's.The.Right.Thing.To.Do.EP07.HDTV.HALFCD-TLF.mkv
/media/Elements/emule/incoming/公平与正义.第10集.Justice.What's.The.Right.Thing.To.Do.EP10.HDTV.HALFCD-TLF.mkv
/media/Elements/emule/incoming/公平与正义.第11集.Justice.What's.The.Right.Thing.To.Do.EP11.HDTV.HALFCD-TLF.mkv
/media/Elements/emule/incoming/公平与正义.第12集.Justice.What's.The.Right.Thing.To.Do.EP12.HDTV.HALFCD-TLF.mkv
/media/Elements/emule/incoming/公平与正义.第1集.Justice.What.The.Right.Thing.To.Do.EP01.HDTV.HALFCD-TLF.mkv
/media/Elements/emule/incoming/公平与正义.第2集.Justice.What.The.Right.Thing.To.Do.EP02.HDTV.HALFCD-TLF.mkv
/media/Elements/emule/incoming/公平与正义.第3集.Justice.What.The.Right.Thing.To.Do.EP03.HDTV.HALFCD-TLF.mkv
/media/Elements/emule/incoming/公平与正义.第4集.Justice.What.The.Right.Thing.To.Do.EP4.HDTV.HALFCD-TLF.mkv
/media/Elements/emule/incoming/公平与正义.第5集.Justice.What.The.Right.Thing.To.Do.EP5.HDTV.HALFCD-TLF.mkv
/media/Elements/emule/incoming/公平与正义.第8集.Justice.What.The.Right.Thing.To.Do.EP08.HDTV.HALFCD-TLF.mkv
/media/Elements/emule/incoming/公平与正义.第9集.Justice.What.The.Right.Thing.To.Do.EP09.HDTV.HALFCD-TLF.mkv

命令中,管道的前半段, "bzip2 -cd
*.dir.zip"是把所有文件都解压了,然后输出到标准输出。所有文件,就是目录树的内容。我们没有真正地把这些内容显示在标准输出上,而是通过管道给了另一个程序。

这接收目录树内容的另一个程序,就是"grep -i justice"。其中的"-i"的含义如下:

$ grep --help | grep -w \-i
Example: /bin/grep -i 'hello world' menu.h main.c
-i, --ignore-case ignore case distinctions

有的同学可能觉得这样不能一行行看,只能去查,有点别扭。不过,事情是这样:
(1)如果你知道自己要找的东西的名字,那么grep的检索条件就是它。或者
(2)如果你不知道自己要找的东西的名字,你就应该仔细想,想完了再来查。大多数事情都可以委托给别人做,唯独找到自己想要什么,必须亲力亲为,不可假手他人。对于需要别人提示才知道这正是自己想要的,我总是想起老师诱导小学生,"你看,啥啥是不是挺好的。"然后,我们就只能相信。

对于这一点,不少人最初都不太容易接受。她们倾向于在讨论的过程中互相理解和支持,并达成一致意见,"啊,原来咱们要找的是这个。"又或者,他们习惯于在工程中提需求的时候说,"你先这么做着,然后看看不行再改。"这路子就跟敬酒的时候说,"我干了,你随意,随我的意。"我们当然可以随客户的意,如果他们为他们不知道自己想要什么的时候你所完成的将要必要抛弃的工作付钱的话。不过,大家通常都愿意只为自己最后见到的东西付钱。

软件工程的著名案例,对比土木工程,从来没人说,"你先把这桥建起来,不合适的话,再转90度试试。"人们往往还没认识到,软件工程跟刮大白一样,人工也是要钱的。又像下棋,你下错了子儿,就要付出代价,而不能撒娇耍赖。

所以,一定要知道--自己想要的是什么。记得当年外教mimi问我们大家,什么是幸福。众说纷芸,吃好吃的啦,能毕业啦,亲人健康啦。我当时正天天抑郁
(现在似乎也未稍减),所以答:知道自己想要什么,这就是最大的幸福。她老人家唯独觉得我说得挺在理的。不过最后我期末成绩也不怎么样,大概她觉得那也不是我所追求的幸福吧。

跟索引和压缩那步一样,我们没有先把zip里的东西解出来
(你列一下zip里的目录就知道了,里面根本没有文件),然后再去grep,而是通过管道把解压的结果传给了grep。

如无必要,勿增实体。

4. 管道

完成以上步骤就能够保存移动硬盘的目录树和检索了,这一小节只是做个游戏。

$ find . | bzip2 -c | bzip2 -cd
.
./250porcelain.dir.zip
./my_passport.dir.zip
./backup001.dir.zip
./Elements.dir.zip
./gold.dir.zip
./lniu.dir.zip

上述命令以管道分隔成了三个部分。第一部分find,列出目录树;第二部分把目录树的文本压缩了;第三部分把传来的压缩的东西解开,然后写到标准输出上。在管道里,目录树内容的文本先压缩了,然后又解压了。

熟悉windows下的C语言的同学可能会想到,这管道先是处理了ascii-text,然后又处理了二进制,到底它是二进制的啊,还是纯文本的呢?这个问题请你自己查查吧,挺有意思的。

5. 如无必要,勿增实体 以外

计算机就像所有的艺术一样,充满了相互矛盾的各种信条,比如这条就是。一方面,我们相信,在程序设计、脚本写作中,应该尽量避免中间的分支出去的东西。类似的思想,在程序设计中,我们就要减少变量,把表达式的值直接传递给函数或者下一个表达式。比如,不写成:

a = 10 + b;
printf ("%d", a);

而是写成:

printf ("%d", 10+b);

但是另一方面,我们有时候又相信这个信条的反面:过早优化是万恶之源。在这个时候,我们相信,应该先对问题建模,然后再归约。建模和归约两个步骤合并成一个,往往是造成问题的原因,归约后的模型通常不那么易读了。越是聪明人,越熟悉的技术路线
(或者只知道这一两种技术路线),越倾向于把归约视为理所当然,而我们又经常没有聪明到可以像控制直接映射的模型那样控制归约后的模型。典型的例子是,当你脱口而出,"这不就是ABCD嘛",尤其是语带不屑的时候。

所以,如无必要,勿增实体 之外,我们还得知道事情的反面--过早优化是万恶之源。

PS.
本文的命令在 emacs eshell 不好使,因为 eshell 对管道支持不充分,shell-mode则没有问题。

--------------------


博客会手工同步到以下地址:


[http://giftdotyoung.blogspot.com]


[http://blog.csdn.net/younggift]

No comments: