BASH下改变PWD的另一种思路

有很多时候希望能通过命令改变当前进程的工作目录。
例如我曾厌倦于终端下很麻烦的才能从很深的子目录中切换到项目的根目录,没有办法让我通过一条简单的命令就直接做到么?我那时的想法是写一个sh文件,然后丢到PATH里,然后执行。但是我发现这样是行不通的,这个shell脚本会作为子进程执行,我尝试过export变量,然而如果了解UNIX原理的话会知道子进程是没法改变父进程的环境变量的,认识到这个事实让当时的我很沮丧。

最近在BYR论坛上有人提问,需求和我当初差不多,我有拾起了这个问题。我需要的是在我当前的进程上下文执行的程序来完成工作目录的转变,那么alias行么?可惜的是bash的alias不能加参数。source行么?这样做倒是能让程序由当前进程执行,但是必须这样用source scriptname,很明显没人会对这个结果满意。我当时就回复,这个功能是不可能实现的,自以为是的写了很长一段,现在看来奇怪的是居然没人反驳我。

最近遇到需求要用BASH,于是打算把SHELL捡起来,打算从BASH的man开始看。五千多行,2天多已经看了一半了,然而我几天前却以为需要几个月。于是在man page中意外的找到了解决办法

方法就是用sh的function,function会比alias和builtin先执行,并且是在当前上下文执行,这样需求可以完美的实现。
我立即把自己当时的想法写出来,那时候是为了方便的回到rails项目的根目录,但是现在已经很久不动ruby了。
代码如下:

function cd() {
    if [[ ! $PWD =~ ^$HOME ]]
    then
        builtin cd $1
        #echo "changed into here:" $PWD
    elif [[ $# -ne 0 ]]
    then
        builtin cd $1
        #echo "switched into here:" $PWD
    else
        while [[ "$PWD" != "$HOME" ]]
        do
            [[ -e .project_root ]] && break
            builtin cd ..
        done
    fi
}


把这个函数放在.bashrc里就可以。项目的根目录手动建一个.project_root文件。这时当在项目的子目录中直接cd的话就会回到项目根目录而不是用户的HOME目录。

顺便把bash的启动文件理清了一下
.bash_profile 当bash作为login interactive shell读取
.bashrc 当bash作为nonlogin interactive shell读取
$BASH_ENV 当bash作为noninteractive shell读取(当然需要这个变量存在,并且的确是一个可读文件)
ssh登录时读.bashrc
当bash作为sh启动时有点不一样
.profile login interactive shell
$ENV nonlogin interactive shell
noninteractive shell不会读任何启动文件
ssh登录时不读任何文件

有的时候困难看似不可战胜,其实可能这时的你对困难了解的还不够多。

python descriptor的妙用

看bottle的源码时发现的。

import functools
import random

class cache(object):

    def __init__(self, func):
        functools.update_wrapper(self, func, updated=[])
        self.getter = func

    def __get__(self, obj, cls):
        value = self.getter(cls)
        setattr(cls, self.__name__, value)
        return value


class A(object):
    @cache
    def m(cls):
        return "this is a methods %d" % random.randint(1,9)

a = A()
print a.m



cache对象用__call__方法,所以可以作为一个decorator。将类A中方法m作为self对象的func属性。注意cache()返回产生的是一个对象,也就是被修改过的self,这个对象在类A中和名字m关联。也就是原来的方法m已经被这个对象替换掉了,当然m对象依然有一个指向原函数的引用。

然后产生一个类A的实例a,当第一次访问a的m属性的时候会调用m的__get__函数,也就是m是一个descriptor。在__get__函数的内部对A新建了一个和m同名的对象(同名的原因就是先开始用update_wrapper将方法m的__name__拷贝到了self的__name__),这个对象的值就是函数m的返回值。假设这里返回的是"this is a methods 3",之后的多次调用会发现函数返回的是一样的值。从而也就实现了方法变为对象的静态属性。

这里可以在__get__函数后定义__set__函数,这样m就变成一个data descriptor了,然后在用上面的方法会发现结果一样。因为这时候属性m还是在A.__dict__中。

如果把setattr(cls, self.__name__, value)换成setattr(object, self.__name__, value)。然后的每次引用m.a就会发现值不同,因为data descriptor在查找链中的优先级要比实例的原生属性高

再见,胡子大叔

 

我被C及Unix的简洁与强大所深深的折服,感谢您和ken为我们带来的杰作,至少使我个人不至于对未来毫无理想。

sicp 练习 1.23

10000000改进函数

(define (find-devisor n test-devisor)
  (cond ((> (square test-devisor) n) n)
	((divides? test-devisor n) test-devisor)
	(else (find-devisor n (next test-devisor)))))

(define (next n)
  (if (= n 2)
      3
      (+ n 2)))


10000000 times 1000
---------------------
10000019 0.341
10000079 0.343
10000103 0.342

100000000 times 1000
---------------------
100000007 1.079
100000037 1.048
100000039 1.035

1000000000 times 500
---------------------
1000000007 3.708
1000000009 3.692
1000000021 3.81

10000000000 times 250
---------------------
10000000019 12.088
10000000033 11.94
10000000061 11.956


100000000000 times 100
---------------------
100000000003 37.91
100000000019 38.44
100000000057 38.14

1000000000000 times 30
---------------------
1000000000039 118.967
1000000000061 121.333
1000000000063 118.9

10000000000000 times 10
----------------------
10000000000037 386.7
10000000000051 384.5
10000000000099 385.8

用上面的结果列表:

num avg-1 avg-2 ratio-1 ratio-2 avg-1/avg-2
10000000 0.5523 0.345    /    / 1.601
100000000 1.741 1.054 3.152 3.055 1.652
1000000000 6.14 3.737 3.527 3.546 1.643
10000000000 20.453 11.995 3.331 3.210 1.705
100000000000 65.4 38.163 3.198 3.182 1.714
1000000000000 208.3 119.733 3.180 3.137 1.740
10000000000000 655.167 385.667 3.145 3.221 1.699

可以看到比值并不是2,一个直观的解释是我们定义的next是一个函数,对一个数的检测要调用next√n / 2 次,造成以一定的开销损耗。可以观察到,其实我们找出来的素数都是奇数,也就不需要为2单独判断,于是可以把next函数改为(+ test-divisor 2),再进行一次实验。

 

10000000 times 1000
---------------------
10000019 0.276
10000079 0.275
10000103 0.282

100000000 times 1000
---------------------
100000007 0.866
100000037 0.868
100000039 0.868

1000000000 times 500
---------------------
1000000007 3.054
1000000009 3.084
1000000021 3.058

10000000000 times 250
---------------------
10000000019 9.956
10000000033 10.292
10000000061 10.18


100000000000 times 100
---------------------
100000000003 32.36
100000000019 33.35
100000000057 32.49

1000000000000 times 30
---------------------
1000000000039 101.333
1000000000061 103.4
1000000000063 102.433

10000000000000 times 10
----------------------
10000000000037 319.6
10000000000051 331.1
10000000000099 341.9

 

num avg-1 avg-2* ratio-1 ratio-2* avg-1/avg-2*
10000000 0.5523 0.277    /    / 1.994
100000000 1.741 0.867 3.152 3.130 2.008
1000000000 6.14 3.065 3.527 3.535 2.003
10000000000 20.453 10.143 3.331 3.309 2.016
100000000000 65.4 32.733 3.198 3.227 1.998
1000000000000 208.3 102.389 3.180 3.128 2.034
10000000000000 655.167 330.867 3.145 3.231 1.980

可以看到调整之后的比例基本都在2的附近,所以可以肯定损耗的原因是next函数带来的开销了。

 

 

sicp 练习 1.22

本来我都是在目录下完成练习,不过这道题实在比较特殊,光用文本解答可能效果不好。

我在这道题上卡了蛮久,一是因为最近放假,荒废起光阴来有了些冠冕堂皇的借口;二是这道题及后面几道相关的题并不像前面的练习一样把代码写出来就完事了,需要的是一种探索式的方法,这种方法可能是科学研究中最基本的,而这也恰恰是我所没有的。

题目里告诉我们大多数lisp实现都包括一个runtime过程,能以微秒的单位返回系统已运行的时间。坏消息是我用来做练习的guile并没有这个函数,好消息是我在这里[1]找到了一个替代方案

 

(define (runtime) (tms:clock (times)))

题目的要求是给定一个起始数字,检测连续一段范围内奇数的素性。并且题目里给出了一段代码,要求你调用这段代码来完成练习。

 

(define (timed-prime-test n)
  (newline)
  (display n)
  (start-prime-test n (runtime)))

(define (start-prime-test n start-time)
  (if (prime? n)
      (report-prime (- (runtime) start-time))))

(define (report-prime elapsed-time)
    (display " *** ")
    (display elapsed-time))
  elapsed-time)

这是我给出的实现:

(define (search-for-primes start range)
  (if (> range 0)
      (timed-prime-test start))
  (if (> range 0)
      (search-for-primes (+ start 2) (- range 2)))

 

问题是在我看来这段代码并不好用,首先题目中这几个函数都没有返回值,也就是他们都是带有某种副作用的过程。没有返回值也就意味这没法根据不同的返回值进行不同的处理。并且题目中要求找出分别大于1e3, 1e4, 1e5, 1e6的三个最小的素数,那么只能给range指定一个足够大的数让它能至少包括三个素数,虽然带有一定的人工成分,但是完成这个任务依然是比较容易的。然而有一个棘手的问题是现在的电脑运行的太快,以至于题目中给出的几个数都被cpu轻松运算完,结果基本上都是0。解决的方法是把数加大,我简单乘了1w。

10000000
---------------------
10000019
10000079
10000103

100000000
---------------------
100000007
100000037
100000039

1000000000
---------------------
1000000007
1000000009
1000000021

10000000000
---------------------
10000000019
10000000033
10000000061

100000000000
---------------------
100000000003
100000000019
100000000057

1000000000000
---------------------
1000000000039
1000000000061
1000000000063

10000000000000
----------------------
10000000000037
10000000000051
10000000000099

我没有列出每个数runtime运行的结果,因为我发现对于小的数来说结果太有浮动性,任意取一次恐怕获得的结果不太好,于是又了运行多次求平均的简单想法。

 

(define (get-avg-runtime n times)
  (define (sum-runtime times sum)
    (if (= times 0)
	sum
	(sum-runtime (- times 1) (+ sum (timed-prime-test n)))))
  (/ (sum-runtime times 0) (+ times 0.0)))

然后把上面几个函数稍微改一下让他们返回值,对于已知的每个素数都运行一次,对越小的数取越大的times,得到结果。虽然结果依然会有一定的浮动,但浮动范围已经很小了。

10000000 times 1000
---------------------
10000019 0.55
10000079 0.549
10000103 0.558

100000000 times 1000
---------------------
100000007  1.743
100000037  1.746
100000039  1.734

1000000000 times 500
---------------------
1000000007 6.146
1000000009 6.14
1000000021 6.134

10000000000 times 250
---------------------
10000000019 20.416
10000000033 20.488
10000000061 20.456

100000000000 times 100
---------------------
100000000003 65.04
100000000019 65.59
100000000057 65.57

1000000000000 times 30
---------------------
1000000000039 211.47
1000000000061 206.6
1000000000063 206.83

10000000000000 times 10
----------------------
10000000000037 653.0
10000000000051 656.0
10000000000099 656.5

得到结果后,进行一个简单的统计#1

number avg ratio
10000000 0.5523     /
100000000 1.741 3.152
1000000000 6.14 3.527
10000000000 20.453 3.331
100000000000 65.4 3.198
1000000000000 208.3 3.180
10000000000000 655.167

3.145

√10大概是3.162, 基本上可以对题目做肯定回答,运行的步骤和时间成正比。

 

 

[1] http://community.schemewiki.org/?SICP

#1 这里得到的时间的单位据说是毫秒,但我感觉不对,不过单位对这个练习没有任何影响。

 

nginx源码学习0

因为前段时间想写个http proxy。自己实验了一下,原理很简单,浏览器把http头发送到指定的代理服务器端口,其中第一行GET/POST 后面跟的是完整的url地址。

自己用python2和nodejs都实现了一遍,代码都不超过50行。但有一些问题我实在解决不了,例如http1.1的流水线处理,http1.1的长连接的具体原理。还有目前我的proxy构架都是单服务器端的,也就是希望client端直接连接我的服务。以前看到GAP的构架是一个本地服务端和一个远程服务端感觉还很奇怪,现在我知道如果不能直接通过端口连接服务的话,用http把请求传到远程服务上是唯一能实现的代理方法。然而这个方法我总归不太喜欢,我对网络编程的理解只处于最初级的阶段,想起来nginx来,就把源码下下来看。

nginx的源码还是很清晰的,虽然作者貌似极度不喜欢写注释,但基本不考虑windows也让nginx的代码少了很多令人讨厌的条件编译。配上ctags和vim,至少我在代码导航上没有任何困难。

 

由于是第一次看这种大项目的源码,经验还是又从头积累。例如很多项目都是实现了自己的内存池,基础数据结构之类的,那么先看这些部分就是比较好的选择,从较高的高度来理解一个函数的作用,也就是所谓抽象也是阅读代码中必须的。

 

内存对其:
nginx中很多数据结构都是要求强制对其的,好处是cpu可以以最少的次数从内存中取出数据,坏处是浪费少许内存。time & space tradeoff,如何权衡看具体的应用了,像web服务器这类的高负载高响应程序当然选择时间了。

#define ngx_align(d, a) (((d + (a-1)) & ~(a-1))

方法很巧妙,可能这也是c代码中的惯例方法,但对于我是第一次接触,所以看到这段代码还懵了一段时间。

两个参数d和a,d代表数据,a代表对其单位。假设a是8,那么用二进制表示就是1000,a-1就是0111. d + a-1之后在第四位可能进位一次(如果d的前三位不为000,则会进位。反之,则不会),~(a-1)是1111...1000,&之后的结过就自然在4位上对其了。注意二进制中第四位的单位是8,也就是以8为单位对其。

可以用python验证一下

>>> a = 17

>>> d = 8

>>> (a + d-1) & ~(d-1)

>>> 24

>>> bin(_)

>>> '0b11000'

>>> bin(d-1)

>>> '0b0111'

vim的paste

今天群里突然讨论起vim来,我当然兴致勃勃的跟着参与。

有人抱怨说vim的copy&paste很莫名其妙。复制的时候如果内容带换行vim的行为会很奇怪。比如:

1
2
3
-----file1.txt-------

a
b
c
------file2.txt--------

如果在windows的gvim下复制file1的第二行,也就是2^y的话,在file2下paste产生的情况是:

a
2
b
c

的确很奇怪,不过分析行为,可以立即确定这是windows下的paste映射成了P,也就是向前黏贴。

如果只用p呢?那么情况就变为

a
b
2
c

这当然符合我们的预期,不过有的时候我想要产生这样的结果怎么办?

a
b2

c

在vim下折腾发现,如果行末带换行,paste的行为会以行为单位进行黏贴,比如p就会把要拷贝的内容插入到当前行下面,而P则插入到上面。而行不带换行,则会插入到行内光标处的前后。也就是vim对不同的内容会产生不同的paste行为,那么如何改变默认的行为呢?

为了这个问题一下午就搭进去了@_@

总结出来的结论就是vim用寄存器来保存拷贝的内容,当使用y命令的时候vim会根据当前的动作设置寄存器的类型。寄存器一共有三种类型,c(字符型) l(行型)b(块型),比如用V复制的行会导致相应寄存器变为l型,而v会导致寄存器变c型。不同的类型就导致前面不同的黏贴行为。

下面是几个相关的函数,regset,getregtype,getreg,通过他们就能动态的改变寄存器的类型,从而产生符合自己要求的行为。

期间群里一位大大发了个地址,才知道vim是寄存在googlecode上的,以后也可以比较方便的翻阅source的change啦~

python中有关属性访问的下划线方法

对象本身可以定义__getattribute__()、__getattr__()、__setattr__()、__delattr__()等方法,以决定存取属性的行为:
def __getattribute__(self, name)
def __getattr__(self, name)
def __setattr__(self, name, value)
def __delattr__(self, name)

 __getattribute__()最容易理解,一旦定义了这个方法,任何属性的寻找都会被拦截,即使是那些__xxx__的內建属性名。

 __getattr__()的作用,是作为寻找属性的最后一次机会,如果同時定义有__getattribute__()、__getattr__()在尋找特性時的順序是:
1 如果有定义__getattribute__(),则返回__getattribute__()的值
2 在产生的实例的类的__dict__中寻找是否有相符的属性名。如果找到且是一个descriptor,返回__get__ ()方法的值。如果是個非descriptor ,則進行第2步
3 在实例的__dict__中寻找是否有相符的属性名,如果有則返回值
4 在产生的实例的类的__dict__中寻找是否有相符的属性名。如果不是非descriptor則直接返回属性值。如果是个只读descriptor(此時一定是仅具有__get__()方法),則返回__get__()的值
5 如果实例有定义__getattr__(),则返回__getattr__()的值
6 如果实例沒有定义__getattr__(),则丟出AttributeError

取得属性順序记忆的原则是:实例的__getattribute__()、資料描述器的__get__()、實例的__dict__、非資料描述器的__get__()、實例的__getattr__()。

__setattr__()的作用,在於攔截所有對實例的特性設定,如果對實例有個設定特性的動作,則設定的順序如下:
如果有定義__setattr__()則呼叫,如果沒有進行下一步
在產生實例的類別上,看看__dict__是否有相符合的特性名稱。如果找到 且實際是個資料描述器,則呼叫描述器的__set__()方法(如果沒有__set__()方法則丟出AttributeError),如果不是則進行下一步
在實例的__dict__上設定特性與值

設定特性順序記憶的原則是:實例的__setattr__()、資料描述器的__set__()、實例的__dict__。

__delattr__()的作用,在於攔截所有對實例的特性設定,如果對實例有個刪除特性的動作,則刪除的順序如下:
如果有定義__delattr__()則呼叫,如果沒有進行下一步
在產生實例的類別上,看看__dict__是否有相符合的特性名稱。如果找到 且實際是個資料描述器,則呼叫描述器的__delete__()方法(如果沒有__delete__()方法則丟出AttributeError),如果不是資料描述器則進行下一步
在實例的__dict__上找看看有無相符合的特性名稱,如果有則刪除,如果沒有則丟出AttributeError

刪除特性順序記憶的原則是:實例的__delattr__()、資料描述器的__delete__()、實例的__dict__。

原始来源:http://caterpillar.onlyfun.net/Gossip/Python/AttrAttribute.html

python文章资源集合

IBM的py3000简介,但是也包括一些高级内容

http://www.ibm.com/developerworks/linux/library/l-python3-1/
http://www.ibm.com/developerworks/linux/library/l-python3-2/

python对象和属性
http://www.cafepy.com/article/python_types_and_objects/python_types_and_objects.html
http://www.cafepy.com/article/python_attributes_and_methods/python_attributes_and_methods.html
http://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python

felix推荐的python必读文章
http://feilong.me/2011/01/recommended-entries-for-you-to-master-python

Python Tips, Tricks, and Hacks
http://www.siafoo.net/article/52
http://stackoverflow.com/questions/101268/hidden-features-of-python
有趣加实用,链接里还有几篇有价值的文章。

IBM的python元类编程教程
http://www.ibm.com/developerworks/linux/library/l-pymeta/index.html
http://www.ibm.com/developerworks/linux/library/l-pymeta2/
http://www.ibm.com/developerworks/linux/library/l-pymeta3/index.html、

python3的元类编程
http://www.artima.com/forums/flat.jsp?forum=106&thread=236234
http://www.artima.com/forums/flat.jsp?forum=106&thread=236260

Guido的文章
http://www.python.org/doc/essays/

cool command在此集结{^_^}

学rails时候按照书里的步骤做,需要把这个地址里面的图片全下过来,想到了wget,经过漫长的折腾,终于搞定。

 

wget -nd -r http://media-origin.pragprog.com/titles/rails4/code/depot_b/public/images/ -A .jpg,.png

-nd --no-directories
代表不产生目录

-r
递归下载

-A --accept
后接用逗号分割的模式列表,选择需要下载的文件,相应的有相反的选项-R --reject

reference:

http://roclinux.cn/?p=2107  其实你不懂wget的心系列文章(适合入门,快速了解wget的基本功能)

man wget(推荐)

 

 

gnu readline
 
删除
ctrl + d      删除光标所在位置上的字符相当于VIM里x或者dl
ctrl + h      删除光标所在位置前的字符相当于VIM里hx或者dh
ctrl + k      删除光标后面所有字符相当于VIM里d shift+$
ctrl + u      删除光标前面所有字符相当于VIM里d shift+^
ctrl + w      删除光标前一个单词相当于VIM里db
ctrl + y      恢复ctrl+u上次执行时删除的字符
ctrl + ?      撤消前一次输入
alt  + r      撤消前一次动作
alt  + d     删除光标所在位置的后单词
 
移动
ctrl + a      将光标移动到命令行开头相当于VIM里shift+^
ctrl + e      将光标移动到命令行结尾处相当于VIM里shift+$
ctrl + f      光标向后移动一个字符相当于VIM里l
ctrl + b      光标向前移动一个字符相当于VIM里h
ctrl + 方向键左键    光标移动到前一个单词开头
ctrl + 方向键右键    光标移动到后一个单词结尾
ctrl + x       在上次光标所在字符和当前光标所在字符之间跳转
alt  + f      跳到光标所在位置单词尾部
 
 
替换
ctrl + t       将光标当前字符与前面一个字符替换
alt  + t     交换两个光标当前所处位置单词和光标前一个单词
alt  + u     把光标当前位置单词变为大写
alt  + l      把光标当前位置单词变为小写
alt  + c      把光标当前位置单词头一个字母变为大写
^oldstr^newstr    替换前一次命令中字符串   
 
历史命令编辑
ctrl + p   返回上一次输入命令字符
ctrl + r       输入单词搜索历史命令
alt  + p     输入字符查找与字符相接近的历史命令
alt  + >     返回上一次执行命令
 
其它
ctrl + s      锁住终端
ctrl + q      解锁终端
ctrl + l        清屏相当于命令clear
ctrl + c       另起一行
ctrl + i       类似TAB健补全功能
ctrl + o      重复执行命令
alt  + 数字键  操作的次数 

ldd查看文件有那些so依赖