Sunday, November 30, 2008

[Python]实现Console下路径输入的补全

因为要用脚本处理一些log,每次运行都要输入待处理的log的路径。可是我存放的log路径太复杂 log自己名字也很复杂,输入起来很麻烦。就想在python里借用shell的路径补全功能。Python里有readline这个module可以提供补全的基本功能,当然需要你自己给它一个completer(text, state)函数从而使它知道如何去补全。另外还有一个cmd module提供简单的命令行界面。用这两者就可以实现我们需要的功能。
import cmd
import string, sys
import os
import readline
class CLI(cmd.Cmd):
def __init__(self):
cmd.Cmd.__init__(self, "tab")
self.prompt = 'input the path:'
self.path = None
readline.set_completer_delims('/\\')
def onecmd(self, path):
if os.path.exists(path):
self.path = path
return path
else:
print "%s not exists"%(path)
return None
def complete(self, text, state):
if state == 0:
origline = readline.get_line_buffer()
line = origline.lstrip()
stripped = len(origline) - len(line)
begidx = readline.get_begidx() - stripped
endidx = readline.get_endidx() - stripped
self.completion_matches = self.completepaths(text, line, begidx, endidx)
try:
return self.completion_matches[state]
except IndexError:
return None
def completepaths(self, text, line, *ignored):
(head, tail) = os.path.split(line)
matches = []
if head == "":
p = "."
else:
p = head
if os.path.exists(p):
filelist = os.listdir(p)
matches = [f if os.path.isfile(os.path.join(head, f))\
else f+os.sep for f in filelist if f.startswith(tail)]
return matches
def get_path(self):
self.cmdloop()
return self.path
def get_path():
cli = CLI()
return cli.get_path()

这里我们继承了Cmd类。但是重载了complete这个函数。这个函数是在用户按下tab键呼叫补全的时候被调用。
使用这个小程序只要import这个文件以后直接调用get_path就可以了.
补充:后来发现了一个小bug已经做了修改。cmd module里面在cmdloop()这个函数里就设定了completer_delims (分隔符)。问题在于默认的分隔符里面有很多合法的linux文件名字符比如 “-”这样会导致tab补全的时候出一些问题。 所以我们需要在这之前设置好。这里我们就在__init__初始化的时候设置成/以及\。

Tuesday, November 25, 2008

[Linux]几个常用的网络相关的工具

  • ttcp
    测量两台主机之间的tcp/udp throughput
    实例:
    第一步1: 在receiver 主机上打开ttcp, 进入接收模式
    [receiver:~]$ttcp -r -v -s
    ttcp-r: buflen=8192, nbuf=2048, align=16384/0, port=5001  tcp
    ttcp-r: socket
    第二步2: 在sender端向receiver发送文件测试传输速度
    [sender:~]$ttcp -t receiver < testfile
    ttcp-t: buflen=8192, nbuf=2048, align=16384/0, port=5001 tcp -> receiver
    ttcp-t: socket
    ttcp-t: connect
    ttcp-t: 20197 bytes in 0.03 real seconds = 740.32 KB/sec +++
    ttcp-t: 3 I/O calls, msec/call = 9.09, calls/sec = 112.60
    ttcp-t: 0.0user 0.0sys 0:00real 0% 0i+0d 0maxrss 0+2pf 4+1csw
  • iperf
    iperf也是测量网络throughput的常用工具之一. 用法和ttcp类似.
  • iptables
    iptables是Linux下的软件firewall
    https://help.ubuntu.com/community/IptablesHowTo
    查看当前iptables的设置
    $sudo iptables -L
  • ipfw
    ipfw其实是FreeBSD底下的软件firewall。可以非常方便的用来traffic shaping.
    实例:
    • 如下命令添加一个virtual pipe用于所有inbound的ip traffic。并且设定这个pipe bandwidth为50Kbit/s buffer size为50
      $sudo ipfw add pipe 1 ip from any to any in
      $sudo ipfw pipe 1 config bw 50Kbit/s queue 50
      
    • 如下的命令模拟ADSL:
      sudo ipfw add pipe 3 ip from any to any out
      sudo ipfw add pipe 4 ip from any to any in
      sudo ipfw pipe 3 config bw 128Kbit/s queue 10 delay 1000ms
      sudo ipfw pipe 4 config bw 640Kbit/s queue 30 delay 1000ms
      
    • ipfw除了使用pipe来模拟link,还可以使用queue来模拟队列。等我知道怎么用了再来写。
  • systat
    Freebsd下监视系统的命令,可以显示CPU、I/O、内存、虚拟内存、mbufs、磁盘IO、网络状态等信息等。
    用法:
    systat [-display] [refresh-interval]
    • display 为我们所要显示的信息项目,我们也可以在进入 systat 后通过输入“:display”变更显示项目。可选项:
      pigs 显示目前系统中使用 CPU 最多的行程名称。如果所有行程的 CPU 使用量未满 100%,则多出来的部份显示为 IDLE。icmp 统计目前 ICMP 封包的进出情形。icmp6 显示 IPv6 的 ICMP 封包进出情形。ip 显示 IP 层的封包统计及 UDP 封包信息。ip6 和 IP 一样,但只显示 IPv6 的封包。tcp 显示 TCP 的封包统计。iostat 显示 I/O 状况统计,并分类为各种模式显示。swap 显示目前各个储存空间上的虚拟内存的使用情形。mbufs 显示 mbufs 被使用的状态。vmstat 这是我们最常用的显示模式,它显示了最多的信息,包含 I/O、虚拟内存、mbufs、网络等信息。netstat 显示网络的使用情形。ifstat 显示各个网络适配卡的使用情形。
    • refresh-interval 参数是需要多长时间采样一次系统数据输出到屏幕,单位是秒。
    实例:
    systat -v 1
    我们打开v选项(vmstat的缩写),每隔1s更新一次。
  • netstat 可以用来查看当前网络所有链接, 也可以用来查看路由信息,比如
    $ netstat -nr
    Routing tables
    
    Internet:
    Destination        Gateway            Flags        Refs      Use   Netif Expire
    default            10.1.10.1          UGSc           23       36     en0
    10.1.10/24         link#4             UCS             6        0     en0
    10.1.10.1          0:22:2d:7b:12:fa   UHLWIir        24       22     en0    134
    10.1.10.10         80:ea:96:e4:f6:93  UHLWI           0        7     en0   1190
    10.1.10.11         d8:d1:cb:91:6d:aa  UHLWI           0        0     en0    691
    10.1.10.15         8c:2d:aa:5a:a7:1f  UHLWI           0       58     en0    733

Monday, November 24, 2008

emacs与vi常用指令对照

这个学期的Type Systems需要学习用Twelf这个语言。简单的说是CMU搞出来的一种(目前主要用于教学的)Logic Programming的语言。因为它提供了emacs的mode,会比调server出来运行要方便快捷。再加上Carl一个劲的鼓吹Computer Scientists是使用emacs的而不是vi。我正好用这个机会push自己多练练emacs。在这个帖子里集中记录我常用到的指令以及和vi的对照。毕竟vi短小精悍而且和less之类的指令相通不可能完全不用。
操作 emacs vi
文件操作:
打开文件C-x,C-f,<filename>:sf <filename>
保存C-x,C-s :w
退出C-x,C-c:q
编辑
选择C-space (设立标记)v (进入visual模式)
拷贝M-wy
剪切C-wd
粘贴C-yP
撤销上一个指令C-x,uu
删除一行C-kdd
拷贝一行
yy
移动
跳至第n行M-g,g,<n>:<n>
搜索
找到patternC-S,<pattern><patern>
其他
执行n个指令C-u,<n>,<cmd><n> <cmd>

Thursday, November 20, 2008

[Python]使用ansi color code 让console五颜六色

写了一个Python小程序, 用于输出带ansi color code的字符串,使得在支持vt100的终端上能看见五颜六色的输出。

ESC = chr(27)
# 字体
a_default = 0
a_bold = 1
a_italic = 3
a_underline= 4
# 前景颜色
fg_black = 30
fg_red = 31
fg_green = 32
fg_yellow = 33
fg_blue = 34
fg_magenta = 35
fg_cyan = 36
fg_white = 37
# 后景颜色
bg_black = 40
bg_red = 41
bg_green = 42
bg_yellow = 43
bg_blue = 44
bg_magenta = 45
bg_cyan = 46
bg_white = 47

def color_code(a):
return ESC+"[%dm"%a

def color_str(s, *args):
cs = ""
for a in args:
cs += color_code(a)
cs += s
cs += color_code(a_default)
return cs

用法
比如你可以用

print color_str("hello world", fg_red, bg_black, a_bold)

来生成黑底红字的加粗的hello world

Wednesday, November 19, 2008

Screen 小结

screen在mitbbs的linux版上被称为震版之宝。minhong推荐下我用了一下,果然非常好用。
这里先占坑。

基本用法:
  1. 在term下键入screen。会出现一些提示,然后就是先前的shell。这时候与普通shell的唯一区别在于你可以使用ctrl+a d (先按ctrl + a, 然后按d)退出这个screen session。这个session以及在这个session中运行的程序将在没有你的监督下继续运行,哪怕你是远程登录到这台主机上而远程登录又中断了。
  2. 查看当前主机上运行了多少个session, 以及每个session叫什么名字:
    screen -ls
  3. 继续一个离开了的session.如果当前只有一个session,那么直接输入
    screen -r
    如果有多个session, 而你要重入其中的一个:
    screen -r sessionname
  4. 进入一个session后, 键入ctrl+a k 终止当前session. 不像ctrl+a d只是离开这个session, 使用了ctrl+a k之后你就无法再次重入这个session了
  5. screen -S your_fav_name -d your_cmd:新建一个名为your_fav_name的screen会话并执行你的命令.为一个会话起一个名字的好处包括用这个名字来kill掉这个会话,而不用每次都先screen -ls看你这个会话的id.
注意事项:
  1. 在远程主机上执行screen: 如果你是ssh到远程主机上并直接用screen 执行一个命令,比如:ssh user@host screen cmd,有可能得到Must be connected to a terminal.这个错误。 解决方法:使用 ssh的-t选项
  2. 杀死远端的screen进程:当你远程主机使用screen在执行一个命令的时候,你可以登录到这个主机并resume你的screen会话并用ctrl+a k杀死该会话。但是这样要求手动控制而无法使用脚本。一个解决办法是用: "screen -S sessionid -X quit" 向远端主机发送screen窗口命令cmd 。 比如quit的话就是结束这个session。
  3. detach远端的screen 进程。 比如你回家之前在office的机器上用screen执行一个命令且没有detach。当你从家里登录office机器想resume则会被告知,该session没有被detach你无法直接在家resume进去。可以发送用: "screen -S sessionid -X detach"命令过去将其detach,然后再实现重入。
  4. 输入backspace的时候总是出现“Wuff Wuff!“这样的讨厌提示。以及del/backspace工作不正常。可以参见http://ubuntuforums.org/archive/index.php/t-90910.html .我的经验是设置TERM变量。bash 底下可以alias screen='TERM=screen screen',tcsh底下可以alias screen 'env TERM=screen screen'

Thursday, November 13, 2008

[Python]Quine(自产生程式) in Python

Quine是指一个程序,它的输出为自身的源代码。这个学期的OS课里读到Ken Thompson的paper里说到了这样的一个程序。Ken Thompson在83年因为早年对Unix和C语言的贡献而得到了图灵奖。这篇paper 是他的图灵奖演讲文稿。在paper里他提到他在UC Berkeley念书的时候,同学们都热衷于写这样的程序并相互比赛谁的程序短。并且他还提到,如果你们自己没有写过这样的程序,他强烈建议自己来试一试写这个程序,会比由别人告诉你如何得到这个程序要有收获的多。于是我便没有接着往下读这个paper看Ken是如何完成的而是试图用Python来写一个这样的程序。最后得到了这样的程序:
M="print 'M='+repr(M)\nprint M"
print'M='+repr(M)
print M
Python来完成这个任务事实上是相当简洁的。因为repr()这个函数帮助我们完成了很多的工作。

Sunday, November 09, 2008

SSH一点点经验

使用~/.ssh/config
.ssh/config这个文件就是给大家偷懒用的.比如我可以指定:
Host aaa
        User apc999-majia
        Hostname aaa.foo.bar
这样只要输入ssh aaa, ssh就自动帮你链接aaa.foo.bar并且使用用户名apc999-majia.
也可以使用通配符*以及?
Host aaa*
        User apc999-majia
在config文件中还可以指定其他许多有用的选项, 参见:config中的选项


不能使用公钥验证登陆的问题:
Office的机器从某天开始突然不能允许我用public key来ssh 登陆了。排查了很久:
(1)首先看public key和private key是不是对的 -- 对的
(2)检查要登录的机器那端权限设置是不是正确:~/.ssh 应该是0700, ~/.ssh/authorized_keys应该是0600, --对的
于是使用sshd的debug模式来看一看:
sudo /usr/sbin/sshd -d -p 2000: 实现一个2000端口的one shot debug mode,
这样你可以在客户端 ssh -p 2000 HOSTNAME 来尝试一次登陆以提供debug信息。
然后看sshd的log (/var/log/auth.log) 看到 sshd[9564]: Authentication refused: bad ownership or modes for directory /home/xxxxx 。遂去看了一下$HOME的权限, 原来不知道怎么给了组用户w权限。去掉以后就好了。

另外本机~/.ssh/config权限应该是600

登录被要求输入两次密码 (Password, Response)
这是由于使用了SSH version 1会这样, 比如我们用-1选项手动使用version 1登录
$ ssh -1 foo.bar.net
Password:
Response:
而使用version 2(选项-2)则是
$ ssh -2 foo.bar.net
Password:

大多数SSH client 默认先使用Protocol 2.但是FreeBSD的/etc/ssh/ssh_config中默认Protocol 1,2. 所以我们可以修改/etc/ssh/ssh_config(如果你有权限)或者~/.ssh/config(只能管你自己),使得
#Protocol 1,2
Protocol 2,1

想看登录时候的详细信息,比如本地和远端主机如何交互:
ssh -v foo.bar.net
想看更详细的:
ssh -vv foo.bar.net
想看更更详细的:
ssh -vvv foo.bar.net
想看更更更详细的:
请订阅中国电视报

关掉每次登录前Host Key的检查。就是每次登录一个陌生host前被询问:
The authenticity of host 'foo.bar.net (1.2.3.4)' can't be established.
DSA key fingerprint is dc:28:c4:30:85:11:75:8a:61:53:65:1a:a0:50:5e:32.
Are you sure you want to continue connecting (yes/no)?
修改~/.ssh/config
Host *
StrictHostKeyChecking no
这个选项默认的是ask,就是每次都询问

用一台主机做代理登录另目的主机(foo.bar.net):
修改~/.ssh/config
Host foo.bar.net
User bfan
ProxyCommand ssh foo.bar.net -l 用户名 nc %h 22

选项 -o BatchMode=yes
好处是用于脚本批量登录的时候(比如使用我的multissh.py脚本)的时候因为一些系统问题被提问密码,直接当作错误返回。 这样脚本可以当作登录失败重新尝试.

端口转发(Port Forwarding, Tunneling)
比如使用了如下命令:
ssh -L 1234:host2:5678 host1
那么在登录host1期间,我们的本地主机还会打开一个tcp端口1234并监听,所有1234端口接受到的流量通过安全连接转发给host2的5678端口.
命令行参数为 -L local_listen_port:destination_host:destination_port
或者在.ssh/config设置为
LocalForward local_listen_port destination_host:destination_port
参考SSH Port Forwarding

对某台主机不使用Pubkey登录
如果是一次性的
ssh -o PubkeyAuthentication=no  your_hostname
如果是多次性的
PubkeyAuthentication=no写进~/.ssh/config文件对应的条目中

环境变量问题
http://roumenpetrov.info/articles/locale_env_in_ssh_session.html
我在自己机器上设定环境变量LC_CTYPE=zh_CN.UTF-8, 导致在一些远端服务器上出现一些locale的问题.这是因为/etc/ssh/ssh_config文件中设定了
SendEnv LANG LC_*
所以LC_CTYPE环境变量也被送到了远端,导致出现了一些问题. 把LC_*去掉后就好了

Sunday, November 02, 2008

[Python]使用Python中的Lambda演算功能:Y-Combinator的Python实现

最近接触的东西都比较杂。不过感觉好几个方面比较有收获。一是使用PyQt感觉Qt平台的强大和华丽。对我来说它最大的好处是跨平台:我的Mac和Ubuntu都可以很好的支持,程序不用修改就能顺利的运行在不同平台上。二是可以使用Python, 这个好处就不多说了。

由于这个学期选得Type Systems课,接触了一些Lambda演算和ML语言(想歪的同学请自觉面壁)。前半个学期由于赶paper的缘故都没有花时间在上面,况且前面的内容也不是那么有趣(Constructive Logic算是一个小亮点)。现在讲到了Polymorphism(应该是多态吧?)以后,才发觉这里面的博大精深。Lambda演算是一种与被大家熟知的Turing Machine不同的计算模型,事实上它的历史甚至可以追随到Turing Machine之前。Turing本人也对Lambda演算做出过贡献。比起Turing Machine,Lambda演算在形式上要“简单”,思维更接近数学。但是Lambda演算的计算能力被证明是和Turing Machine的计算能力是等价的。Turing Machine上著名的停机问题也有其Lambda 演算版本。而且这个版本其实由Church比Turing更早提出

Lambda演算的基本形式为λx.body。 这里可以把x看成一个函数的输入参数,body看成这个函数的返回值。不过与普通编程语言中的函数不同的是, x可以为一个普通的值变量,也可以为一个函数,还可以是一个类型。简简单单的定义却有无穷的变化。比如Church在Lambda演算的基础上重新定义了正整数。还可以在Lambda演算的基础上把布尔类型以及正整数类型推广化。比如List就是一种generalized的正整数。

Python提供了Lambda算子的功能。下面我就用Python的Lambda算子来实现不动点算子Y-Combinator。有关什么是Y-Combinator可以看一下wiki。简单的说来,给定一个函数g以及不动点算子Y,我们有Y(g) = g(Y(g))。从而我们可以用不动点算子Y来实现g的递归调用。

Y-Combinator in Python:
Y = lambda f: (lambda x: f(lambda n:x(x)(n)))(lambda x: f(lambda n:x(x)(n)))


这样如果你定义了非递归函数,比如阶乘
g =  lambda factorial, k : 1 if k == 1 else factorial(k-1)*k

或者更容易理解的方式是这样:

def g(factorial, k):
if k == 1:
return 1
else:
return factorial(k-1)*k

就可以用 Y(g) (k) 来递归求k的阶乘。

Ref: http://siddhi.blogspot.com/2007/08/y-combinator-in-python.html

Saturday, November 01, 2008

[Python]Python2.6内建函数(未完)

Python用了很久了,但一直没有系统的学习一下它的内建函数, 也就是在__built-in__这个module里的函数。这些函数都是在直接默认在namespace中不需要import 任何其它module来获得的。所以可以想象,也是最常用最有用的那部分函数。决定今天补上这个差事。也是为了使自己能够熟系很多不那么常用的功能,写出更加有效率的代码。

__import__ 最常使用的import函数会调用__import__。python 提供这个内建函数是为了让用户可以改变import的语法,比如提供自己的import 函数

abs(x) 返回绝对值。按说应该放在math module里面, 但是估计太常用了就放在built-in里面了。同样的还有max, min

all(iterable), any(iterable) 前者返回True当所有iterable元素为“True”,后者返回True当有一个元素为“True”。装13的话可以这么说,一个是全称量词一个是存在量词。不过要注意的是, 我这里True打了引号是因为不仅仅是Boolean的True值。事实上,在Python当中,False, 0, 空的list [], 空的dict {}, 空的tuple () 以及空字符串 "" 都在某种程度上被认为是False。 比如x取值为上述任何一者的时候, not x 都等于 True。这两个是Python 2.5以后出来的新feature

basestring() 是str 和unicode的superclass

bin(x) 输出一个整数的二进制字符串。 比如 bin(9) = ' 0b1001'。 Python 2.6添加的很实用的一个功能。

bool([x]) 把一个值转换为bool值。规则如上在all, any中所述

callable(obj) 如果obj为可调用, 返回True, 否则为False。 比如 callable(lambda x: x**2) = True。 callable([1]) = False

chr(i) 将一个ASCII码转换为对应的字符。ord 为其反函数。 chr(48) = '0', ord('0') = 48。

cmp(x, y) 比较 x 和y, 如果xy返回正数

compile 编译一段python的code。比如 a = compile("print \'Hello World\', '', 'exec') 然后就可以用 exec(a)来得到'Hello World'的输出

complex 创建一个虚数

delattr(obj, name), setattr(obj, name) 比如 delattr(x, 'foobar') 等同于 del x.foobar

dict 创建一个dictionary。 类似用法的还有 tuple, list, set。需要提一下的是dict的用法。比如dict({'one':1, 'two':2})是最基本一种,实践中更加简洁方便的一种写法是 a = ['one', 'two']; b = [1, 2], dict(zip(a, b))。 zip是一个非常神奇的函数,用起来好处多多,可以让代码简洁清晰很多.