20121129

Linux shell脚本,Linux下的西红柿时间管理法 III

Linux shell脚本,Linux下的西红柿时间管理法 III

8. 需要的支持技术

我们说到希望从atq指令的输出中,自动计算出还剩多长时间休息,而不是靠用户自己计算。atq的输出是这样的:

1 3444 Tue Nov 27 18:47:00 2012 r young
2 3445 Tue Nov 27 18:48:00 2012 r young

第1行是提醒的任务,第2行是锁屏的任务。这里已经包含了执行任务的时间,即Tue Nov 27 18:47:00
2012,我们要做的是把这段数据抽出来,跟当前时间做减法。

单纯这么一说,似乎任务很简单,但事实要更复杂一些,一会儿你会看到。在看到任务的时候就确定有哪些支持技术,是非常困难的。难到就像,你还没有迈出第一步,就确定以后的路上会遇到哪些大河要跨过,哪些高山要翻越。不知道以后会有什么困难就无从准备,而那些没有做好准备就遇到的困难,正是扼杀成功的原因。怎么办呢?无他,或者,如果我们有经验,走过一次,那就已经知道了;又或者,有个向导带着我们走,他已经走过一次;又或者,干脆现在走一次,然后就知道了。

在没走之前先猜测一下会怎么样,在走的每一步记录,这正是工程师的基本素养:估算和度量。

9. 探索的过程

我们准备写一个shell脚本,运行的时候显示出当前时刻距离atq中的第二行任务还剩多长时间

9.1 解析数据

要从

1 3444 Tue Nov 27 18:47:00 2012 r young
2 3445 Tue Nov 27 18:48:00 2012 r young

中把"Tue Nov 27 18:48:00
2012"单独取出来,是按约定的语法或格式处理字符串,在编译原理里叫作做解析。解析简单的数据的手段很多,比如sed,比如cut。

我这样实现的:
atq -q r | cut -f2 | cut -d' ' -f 1,2,3,4,5 | sort | sed -n 2p

这是由一组管理连起来的进程组成。"|"就是管道,表示前一个进程的输出作为后一个进程的输入。我们一步一步看。这些进程,包括cut,sort,sed,都是字符串处理常用工具,具体的用法和参数我也记不住,常要看手册或者google。

第一步,atq -q r,表示只列出队列r中的任务。执行效果是:

~ $ atq -q r
3502 Thu Nov 29 19:33:00 2012 r young
3503 Thu Nov 29 19:34:00 2012 r young

第二步,cut -f2,表示取出第二个字段。效果相当于下面这两行:

~ $ atq -q r | cut -f2
Thu Nov 29 19:33:00 2012 r young
Thu Nov 29 19:34:00 2012 r young

只去除了3501和3502两个序号。不要小巧这点工作,这是一个很好的开始,并且表明我们能够操作字符串了。

第三步,cut -d' ' -f
1,2,3,4,5,表示取第1、2、3、4、5字段,并且以空格作为分隔符划分字段,效果如下,切掉了后面的队列名和用户名,离目标更近了:

~ $ atq -q r | cut -f2 | cut -d' ' -f 1,2,3,4,5
Thu Nov 29 19:33:00 2012
Thu Nov 29 19:34:00 2012

第四步,sort,排序,默认是由小到大,效果如下:

~ $ atq -q r | cut -f2 | cut -d' ' -f 1,2,3,4,5 | sort
Thu Nov 29 19:33:00 2012
Thu Nov 29 19:34:00 2012

这是为了下一步有确定的输入,以便挑出正确那行。

第五步,sed -n 2p,选择输出第二行,效果如下:

~ $ atq -q r | cut -f2 | cut -d' ' -f 1,2,3,4,5 | sort | sed -n 2p
Thu Nov 29 19:34:00 2012

小结,我们由atq输出的任务

3502 Thu Nov 29 19:33:00 2012 r young
3503 Thu Nov 29 19:34:00 2012 r young

得到了将会锁屏的时间:

Thu Nov 29 19:34:00 2012

这一时间,将用于下一步的做差计算。慢着,任务的时间和当前时间做差,当前时间是什么?

当前时间可以用date获得,效果如下:

~ $ date
Thu Nov 29 19:18:20 2012

9.2 计算时间差

求时间点 Thu Nov 29 19:18:20 2012 和时间点 Thu Nov 29 19:34:00 2012
的差,当然不能把它们直接相减了事。有的同学可能想到了,可以按上面的方法解析出日期、小时、分钟、秒,然后再做减法。这也能解决,不过由于时间相减是个工程中可以想见的常用的功能,所以,应该有现成的解决方案。这也是个原则,凡是常用的功能,一定有常用的方案或者函数。至于什么是"常用的"功能,你常做小项目,就会知道。

去做,就会明白。就像,怎么成为好人,你希望别人如何对你,别人如何做你会感动,按照那个要求去做,那就是好人了,至少会被别人评价为"好人"。

时间相减这个功能的常用方案,我这样实现:

1 task=$(date -d "$task" +%s)
2 now=$(date -d "$(date)" +%s)
3 diff=$(($task-$now))

第一行中,执行了一个指令"date -d <什么东西>
+%s",这代表把<什么东西>里的时间转化为自1970年1月1日起到<什么东西>这个时间,相差了多少秒。为什么是1970年1月1日而不是1949年呢,因为大家可能还记得C语言和UNIX操作系统诞生于1969-1970年。那个<什么东西>就是"Thu
Nov 29 19:18:20 2012"这样的东西。

执行效果是这样的:

~$ date -d "Thu Nov 29 19:18:20 2012" +%s
1354187900

到底多少秒,我们也并不需要关心,一会儿能用来求差就行了。

"task=$(date -d "$task" +%s)"中代替了<什么东西>的,不是固定的时间,像"Thu Nov 29 19:18:20
2012",而是"$task",它表示一个变量,名字叫做task。这个task是这么来的:

task=$(atq -q r | cut -f2 | cut -d' ' -f 1,2,3,4,5 | sort | sed -n 2p
)

这就像在上面三行代码里,也有个变量赋值给task:

task=$(date -d "$task" +%s)

在等号右边的task的值是"Thu Nov 29 19:18:20 2012",在等号左边的task的值是上面的那个秒数 1354187900。

第2行为now赋值为"此刻"date的秒数。
2 now=$(date -d "$(date)" +%s)

第3行求差得到diff,锁屏时刻的秒数 (距1970年元旦 )和此刻的秒数 (距1970
年元量) 的差:

3 diff=$(($task-$now))

这样,我们就得到了,还有多少秒到锁屏时间,虽不够友好,但是离目标又近了一步。

9.3 格式调整

我不希望这样的结果,还剩778秒到休息锁屏时间,虽然这和12分钟58秒没什么本质区别。我希望它这样提示 12:58。

9.3.1 换算

这个好办,对60做除法,商就是分钟数,余数就是秒数。既然这么显然,那shell一定提供了这样的支持技术,果然一goolge就有。其实上面这些,有不少我忘记了或者记不清的,我也都去google了。关键在于,你得知道那东西存在,才能google。

这里[http://www.computing.net/answers/unix/remainder-operator/6820.html]有一段

: Use the % operator. Check below
: --
: 1. expr 5 / 2 gives 2 as Quotient.
: 2. expr 5 % 2 gives 1 as Remainder.

所以,我这样实现:

minute=$((diff/60))
sec=$((diff%60))

又是变量赋值。

9.3.2 条件判断

但是,还有些别的可能,比如只剩下12分5秒的时候,我不希望显示成

还剩12:5

而是希望得到:

还剩12:05

这需要条件判断。查到shell中的条件判断的语法:

if [cond]; then
fi

当然,前提还是,你得确信知道条件判断这种东西是存在的。

我这样实现:

1 if [ "$sec" -lt "10" ]; then
2 sec=0$sec
3 else
4 sec=$sec
5 fi

9.3.3 连接字符串

上面的代码中,第2行中的"0$sec",把常量字符串与变量相连接,就是直接接起来写,这个我也试了一下,如下:

$ a='hello'
$ b='world'
$ c=$a$b
$ echo $c
helloworld

9.3.4 判断空行

还有另一个可能。如果还有不到一分钟就休息了,atq的输出将只有一行,因为提醒音的那个任务已经执行完了。

我们需要判断输出有几行,或者说,我们需要判断空行。goolge得到:

[http://stackoverflow.com/questions/3061036/how-to-find-variable-is-empty-or-in-shell-script]

: In bash at least: if [[ -z "$var" ]]
:
: the command line "man test" is your friend.

最后一行对应的UNIX谚语是:男人是最好的,原文 "man is the
best"。这里的man,不是男人,而是manual的缩写,unix下的一个指令
man,用来查手册的。尽管大家都心知肚名man不是男人的意思,还是有不少unix/linux发行版中的man指令有个等同的指令,名字叫做woman。恩,而且woman和man不仅平等,而且完全一样。

我这样判断空行:

1 if [ -z "$task" ]; then
2 echo "No more than 1 minute left to have a break."
3 exit 0
4 fi

10 bug修正和功能增加

人生总是只有开始,才知道原来后面有这么多苦难;程序总是开跑才发现,原来总有bug存在。王子和公主从此以后过上无忧无虑的幸福生活,这种事只有在童话里才有。

没啥,逢山开路,遇水搭桥,碰到bug就杀掉。

10.1 排序

atq输出的时候,输出我也不知道是怎么个顺序,所以有了上面的sort指令。最初的版本中是没有的。

10.2 只加入指定的队列

虽然还没有出现这样的情况,但是在其他的时候,或者其他的程序,可能也要使用at任务的队列。所以,我改成只选其中一个队列使用。

atq -q r,只显示队列r中的任务。

at ... -q r,向队列r中添加任务。


11. 秘密

表面上看,上面是演示如何实现西红柿时间管理。没错,西红柿时间管理确实很有效,我常用。但是,正如钢琴老师教你弹的那些小曲子也确实挺好听的,但是她教你弹曲子的目的绝对不仅是欣赏这些曲子本身,而是更注重练习曲目时所得到的能力提升。这个小项目也是一样。

以上这些步骤看起来很流畅,先这样,再那样,然后又如何,最后问题解决了。等到你实践的时候可能略有不同。这种差异的来源是因为:我其实不是盲目地探索的,因为学习过其他的语言、做过其他的小项目,所以能够猜到应该有这样的那样的解决方案存在,所以我是有意去寻找每一步的技术和有计划地逼近终点的。换句话说,我经常地猜到了那里应该有这样的技术存在,然后才去google。而google并不能告诉你你不知道的东西。

如同《怎样解题:数学思维的新方法》[http://book.douban.com/subject/2124114/]这本书所说的,教师解题的过程,并不是盲目的,而是假设或计划将会如何,然后朝着那个方向推理和努力。我们作为学生需要体会的,是在观摩教师解题过程中的那些节点和方向。然后可能还要思考,他是怎么知道到这里就会有这个技术了呢?就像故事里讲的,孩子问雕塑师,你先前怎么知道石头里有个女人的呢。

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

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

[http://giftdotyoung.blogspot.com]

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

20121127

Linux shell脚本,Linux下的西红柿时间管理法 II

Linux shell脚本,Linux下的西红柿时间管理法 II

4. 架框及已有的技术

我原来的代码非常简单,里面包含了大致的框架和已经利用的技术,如下。

1 #!/bin/bash
2 at now + 20 minutes 2>/dev/null <<EOF
3 aplay -q /usr/lib/openoffice/basis3.2/share/gallery/sounds/horse.wav &
4 EOF
5
6 atq | tail -n 1

以上是计时20分钟的,计时5分钟的基本相同,唯上面的20改为5。

第1行,表示将使用bash作为shell解释执行。

第2行,at指令表示增加一个作业,在某个时间执行,具体的时间是20分钟以后。"2>/dev/null"是错误重定向,即如果有错误信息,输出到null设备,也就是忽略它们。这是为了避免有错误的时候出提示信息。我希望程序出错的时候就死掉。

从第2行的"<<EOF"到第4行的"EOF"之间,是当20分钟到了的时候,计算机应该干点啥。第3行,也就是播放那声巨难听的马叫。

以上,就是主要的框架,手动运行脚本时增加一个at作业,然后到时间的时候放点声什么的提醒我。但是仅仅有音乐不足以打断我,所以向eyefoo学习,我准备让计算机到时间的时候把屏幕锁住。

这就涉及到下一个问题,我需要一些新的技术支持我。这也是做不熟悉的小项目的一个重要技巧:不是按需求-分析-设计-实现这样的瀑布或其变形,而是在做完需求和基本框架以后,要做技术原型测试需要的新技术。

5. 需要的支持技术

需要的支持技术包括:锁屏幕,解除屏幕的锁定,放音乐,算还剩多少时间休息。之所以新技术里需要
放音乐,是因为我发现马叫声太难听了,而eyefoo的那几段mid不错,准备拿来用。

算还剩多少时间这个比较麻烦,后面我们再单独谈,先看其他的。

5.1 锁屏幕和解锁屏幕

google了一下,得到以下结果。

[http://stackoverflow.com/questions/8681662/lock-ubuntu-from-shell-script]
Lock
ssh -X user@server "export DISPLAY=:0; gnome-screensaver;
gnome-screensaver-command -l;
UnLock
ssh -X user@server "export DISPLAY=:0; gnome-screensaver;
gnome-screensaver-command -d

然后我手动试了一下,好使。这里想再补充两点。第一,google不是万能的,使用它的前提是你得知道自己要找什么。所以从需求到分析、能找到需要的技术都很重要,它们帮助你知道要找什么。第二,找到以后试一下很重要。不试你不知道它好使,仅仅网友宣称甚至手册宣称都是没用的。亚里士多德还宣称两个铁球不同时落地呢。你得先试一下。此外,试的时候不要在你正准备做的项目里试,另做一个小的项目,专门试这个技术的。一次只解决一个问题,保持最小系统,都是符合14条原则的。

5.2 放音乐

5.2.1 音乐文件

eyefoo的mid在我的Linux机器上放不出声来,因为我的机器里没有这种乐器库。这个说起来就复杂了,跟mid的原理有关,此处略过。总之,你可以有两种方法做出个新的mid来。一种方法是,找个听音牛的,让她告诉你那个曲子的谱,然后你用lilypond或者cakewalk或者Siblius之类的软件按谱做个mid出来。嘴哼哼也行,在旋律上这没什么差别,所不同者,嘴哼哼的不能用mid格式。另一种方法是,用rosegarden打开这个mid文件,编辑一下,换个乐器试试,我换了三角钢琴。

5.2.2 播放

timidity break2.mid

timidity是个Linux下的midi播放软件,命令行的,可以在shell脚本下调用。这就是为什么那些命令行工具会长久存在的原因,它们为shell脚本提供服务。而GUI编脚本就困难多了。等你看了我后面贴的脚本代码,再对比.net之类写的工具,就会发现脚本真是简单了。

当然,这个也得试一下技术原型。

5.2.3 音量

我发现原有的曲子音量有点小,有的时候预告时间快到了那几声听不到,所以查了一下timidity的参数,-A 是调音量的。

当然,这个也得试一下技术原型。下略。

6. 代码

根据需求、既定的框架、新试完的技术,就有了以下代码:

1 #!/bin/bash
2 tomato_path="~/tools/tomato"
3
4 at now + 24 minutes -q r 2>/dev/null <<EOF
5 timidity -A 800 $tomato_path/prebreak2.mid &
6 EOF
7
8 at now + 25 minutes -q r 2>/dev/null <<EOF
9 timidity -A 800 $tomato_path/break2.mid &
10 gnome-screensaver-command -l
11 sleep 300
12 timidity $tomato_path/unlock.mid &
13 gnome-screensaver-command -d
14 20.sh
15 EOF
16

第2行,定义一个变量名字叫 tomato_path,后面在第5行、第9行、第12行分别引用了一次。

第5行,到24分钟的时候,预报时间快到了;第9行,放声音,下课啦;然后在第11行休息300秒,即5分钟;第12行,播上课铃声。

第10行,到时间了,锁屏幕。输入密码的时间通常足够阻止我继续工作的冲动。

第13行,休息结束,开启屏幕。

第14行,自动地再次运行这个脚本,即开始计时25分钟。

这段代码也可以在 [https://github.com/younggift/tomato.git] 找到。

7. 更多需求

这基本就够用了,但是还有不足。指令atq可以列出任务在什么时间将被执行,像这样:

$ atq
3444 Tue Nov 27 18:47:00 2012 r young
3445 Tue Nov 27 18:48:00 2012 r young

但是,我更想直接快速知道还有多少分钟可以工作,而不是根据当前时间和任务时间计算。所以,我们还希望能用脚本解决这个问是。

未完待续

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

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

[http://giftdotyoung.blogspot.com]

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

Linux shell脚本,Linux下的西红柿时间管理法 I

Linux shell脚本,Linux下的西红柿时间管理法 I

1. 西红柿时间管理法是什么

西红柿是一种时间管理方法,用于帮助我们集中注意力的。大致的思路是,集中注意力工作25分钟,在这个期间,拒绝电话、上网、计划外的讨论--一般而言,即使对方是你的老板,告诉他25分钟以后去找他,这样的态度他也容易接受。25分钟以后休息5分钟,无论工作得有多么high,工作有多么重要。花了多少时间不重要,集中注意力才是生产力,因为人的脑力创造性的效果并不与时间成正比。重复以上步骤4次,休息一个较长的时间。

为什么这个时间管理法叫做西红柿呢,因为人们最初使用西方厨房里煮鸡蛋计时器来倒计时25分钟和5分钟。咱们煮蛋的时候一般都是用手表,隔一会看一眼表,差不多到时间了捞出来。这个表面上合理的方法违背了计算机程序设计中的一个比较基础的原则:中断比轮询对CPU的占用率低。不过咱们似乎一向对工作和对生活按两种不同的态度。

西红柿的详细介绍可以参见[http://article.yeeyan.org/view/204108/169539],参考书是[http://book.douban.com/subject/5916234/]。

2. 我为什么要在Linux下写西红柿时间管理工具呢

最初,我写了个非常简单的脚本。大概能实现这样的功能:手动运行一个脚本,到25分钟的时候,机器叫一声,是一声巨难听的马叫,提醒我该休息了;然后手动跑另一个脚本,到5分钟的时候再叫一声。

然后,我遇到了问题。到25分钟的时候,如前所述,我可能干活正high,当时听到马叫的反应就是"啊,时间到了,马上就停",这个"马上",有一次持续达4个小时。我遇到的另一个问题是,开始工作的时候,有时忘记手动运行这个25分钟计时,直到累得不行的时候才想起来,"20分钟怎么这么长啊。"

后来,张和郑两位同学群发了个工具,就是eyefoo[http://eyefoo.com]了。这是个windows下的程序,我需要windows的时候就用它。感觉不错,对比我前面在Linux下遇到的问题,就看出了原来简陋脚本的差距了。

3. 功能需求

第一,我的脚本在25分钟结束的时候提示的"声音还不够大"。需要更强有力的事件阻止我继续工作。这并非你的意志薄弱,而是动物性使然。心理学告诉我们,当我们产生情绪冲动的时候,理智那块的大脑还没来得及得出结论。所以,心理学家又告诉我们,这个时候,延迟和打断就很有效果。你如果特想买某个东西,把它放到购物篮,间隔一段时间,等到付钱的时候,想买的冲动可能已经消失了。基本上,愤怒、想砍人什么的,都可以用这种方式化解。足够强有力的打断,然后一小段延迟。

这种强有力的打断虽然来自我自己,仍然令人不爽。所以,eyefoo做得很好,它提前一会儿告诉你,"快到点了啊。"这人性化多了。

第二,我希望25分钟结束以后,自动计时5分钟,休息5分钟结束以后,自动计时下一轮的25分钟。

第三,我仍然希望它工作在Linux下,而且还用shell来写。

未完待续

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

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

[http://giftdotyoung.blogspot.com]

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