Programming

又遇SNMP

差点被SNMP给整疯了……

先是要从QoS中提取指定信息,要导出到SNMP。这看似简单但做起来相当麻烦,基本上就是左手拿着表格查对应的行和列,右手拿着QoS输出对比,然后脑子里要思考如何提取正则表达式。总共50多项,我是一项一项对出来的,哎,没办法,这根本不可能由机器完成(瞧瞧计算机有多笨!!不过它要是聪明到这种程度我们这些程序员就该失业啦~!)。

然后要考虑整个程序的构架,看似一个简单的程序,如果什么都不考虑直接去写肯定也可以,50多个函数嘛,可问题是怎么把代码组织得更和逻辑?这确实得下一番功夫。一开始我想出一个颇为简洁的构架,后来发现不行,经过修改最后居然保持住了简洁,纯属侥幸~~最后再看看整个程序的构架,依然简洁而又合理,先得意一下。;-)

最后测试时又出问题了,因为我几乎把SNMP忘干净了(Dr. Chen,我对不起你啊~~~!),然后翻怎么才能查询到对应的表项,满头大汗……不过最后终于完成了,还好,挑了几个经典的项放进去测试了一下,一切正常。不过,上头要求把50多项全部放进SNMP里测,我晕,难道我还得一个一个把它们手工输入到snmp配置文件里???岂有此理!我怒了,端出如下一行命令搞定一切:

sed -ne ‘/cmd_map/,/#EOF/ p’ qosscript | grep ‘=>’ | awk -F’=>’ ‘{print $1}’ | while read EN; do echo exec $EN /eos/qosscript $EN; done | sed -e ‘s/‘“‘“‘//g’ >> /etc/snmp/snmpd.conf

任务结束,世界安静了许多。:-)

另外记一下在写这个脚本时用到的一个技巧——用Perl正则表达式匹配多行文本:

local $/;
$output = <$fd> ;

然后就可以这么匹配了:

$output =~ m/class htb . root rate ([0-9]+)Kbit .nsSent ([0-9]+) bytes ([0-9]+) pkt (dropped ([0-9]+).ns*rate ([0-9]+)bit/m

bash变量作用域

bash的变量作用域其实很简单,不过有些时候可能看起来很奇怪。下面简单总结一下。

在默认的情况下,bash的变量都是全局的,除非你使用了local或者typeset命令来改变,那样就可以变成局部的了。

export改变的是变量是否对其子进程可见,并不会影响父进程,如果想要改变父进程中的变量可以用source(或者“.”)。需要额外说明的是,source后面的脚本不能有exit,否者父进程就跟着一起退出了。

在subshell中,对变量的改变不会影响到外面的shell。这似乎很容易理解,不过需要注意的是,一些管道(比如管道的一端是while循环时)也是通过subshell传递的,比如下面这个例子

  1. foo=123
  2. echo 456 | while read line ; do
  3. foo=$line
  4. done
  5. echo $foo


这时就不那么容易觉察了。解决方法是,用重定向去替换管道。

查汉字拼音的程序

马克同学是我们公司的活宝,更是一个喜欢学习汉语的好青年!他自己琢磨着装上了scim,现在还可以用拼音输入法在skype上用汉语向我问好了。说起来比较囧的是,我教他的第一个汉字就是“囧”……因为这是个典型的象形字!我还郑重地告诉他,其实不少中国人自己也不知道怎么念这个字!

有一天喝咖啡时,马克同学问我知道了一个汉字怎么查它的拼音?问得好!在我们看来知道拼音是理所当然的,但对老外们来说确实是个问题。我搜了一下,扔给他一个供小学生使用的查拼音的在线网站。后来想想这样不太好,因为那个网站本身就是中文的……于是我用Python编了这么个小程序,打算下周上班时送给马克同学。

[python]

!/usr/bin/env python

import os,sys
import string
import urllib2
import zipfile

def find_it(x, lines):
ret = []
for line in lines:
if line.find(x)!=-1:
ret.append(line.split(“:”)[0].strip())
return ret

if name == ‘main‘:

if len(sys.argv) != 2:
    print "Ni hao! Chu cuo le!"
    sys.exit(1)

if os.getenv("LANG").upper().find("UTF8") == -1:
    print "Please use UTF-8 locale!"
    sys.exit(2)

try:
    f = open('/tmp/pinyinsfssafd.zip', 'r+')
except IOError:
    req = urllib2.Request('http://blogimg.chinaunix.net/blog/upfile2/080906001029.zip')
    resp = urllib2.urlopen(req)
    zfile = resp.read()
    f = open('/tmp/pinyinsfssafd.zip', 'w')
    f.write(zfile)
    f.close()
finally:
    z = zipfile.ZipFile("/tmp/pinyinsfssafd.zip", "r")
    bytes = z.read('pinyin.txt')
    lines = bytes.split('rn')

    x = sys.argv[1]
    for c in x.decode('utf-8'):
        if c == u'-':
            continue
        if c in string.letters:
            continue
        if c in string.digits:
            continue
        ret = find_it(c.encode('utf-8'), lines)
        if ret:
            print c+" can be read as : "+'/'.join(ret)
    z.close()

[/python]

UNIX 文学

这里有一篇非常精彩的文章,论述了为什么UNIX才是真正能给你自由的操作系统,UNIX的“文学性”,“道”,艺术等等,里面的很多观点说出了我的心里话!强烈推荐!

特摘取几句我喜欢的:

UNIX programmers express themselves in a rich vocabulary of system
utilities and command-line arguments, along with a flexible, varied
grammar and syntax.

The one-size-fits-all, point-and-click,
we've-already-anticipated-all-your-needs world of NT had me yearning
for those obscure command-line flags and man -k.

I wanted to craft my own solutions from my own toolbox, not have my
ideas slammed into the visually homogenous, prepackaged, Soviet world
of Microsoft Foundation Classes.

In a literary light, if UNIX is the Great Novel, Perl is the
Cliffs Notes.

Mastery of UNIX, like mastery of language, offers real freedom. The
price of freedom is always dear, but there's no
substitute. Personally, I'd rather pay for my freedom than live in a
bitmapped, pop-up-happy dungeon like NT.

修复 Linux 文件名的脚本

肯定还有bug,欢迎指正。;-)

(下面的语法加亮有问题。。。)
[bash]

!/bin/bash

posix_chars=”a-zA-Z._0-9-“
bad_chars=”][|#?><*$'"(){}&"

onlyquery=0
only_print=0
only_posix=0
only_whitespace=0
replace_char='
'
append_chars="_2"
remove_dup_underscores=0
allow_recursion=0
start_with_dash=0

function test_name
{
if [ $only_posix -eq 0 ]
then
test_chars="[${bad_chars}]"
ret=0
while(($#!=0))
do
ret=$(($(echo "$1" | wc -l)-1))
echo "$1" | perl -n -l -e "exit 1 if m/[s]/;"
ret=$(($ret+$?))
case "$1" in
"" | - | ${test_chars}) ret=$(($ret+1))
;;
"\") ret=$(($ret+1)) #'' is special !!
;;
esac
shift
done
else
test_chars="[!$posix_chars]"
ret=0
while(($#!=0))
do
case "$1" in
"" | -
| ${test_chars}) ret=$(($ret+1))
;;
esac
shift
done
fi
return $ret
}

function goodname
{
rpl="${2:-
}"
tmp=$(echo -n "$1" | tr '[ trn]' "$rpl")
if [ $only_whitespace -eq 0 ]
then
echo $(echo "$tmp" | sed -e "s/[$bad_chars]/$rpl/g" | sed -e "s/[]/$rpl/g")
fi
}

function nodupunderscore
{
echo "$(echo "$1" | sed -e 's/_
*/
/g')"
}

assume in pwd directory

get_new_name bad_name replace_char append

function get_new_name
{
if test_name "$1"
then
if [ $remove_dup_underscores -eq 1 ]
then
echo "$(no_dup_underscore "$1")"
else
echo "$1"
fi
return
fi
apd="$3"
new_name="$(good_name "$1" $2)"
if [ $remove_dup_underscores -eq 1 ]
then
new_name=$(no_dup_underscore "${new_name}")
fi
if [ -e "${new_name}" ]
then
if [ ${new_name##.} = ${new_name} ]
then
echo "${new_name%.
}${apd}"
else
echo "${new_name%.}${apd}.${new_name##.}"
fi
else
echo "${new_name}"
fi
}

function print_version
{
echo "Copyright (C) WANG Cong, 2008."
echo "Version 1.5"
}

function print_help
{
prog=$(basename $0)
cat <&2
echo >&2
print_help >&2
return 1
fi
test_name “$@”
return $?
else
if [ $only_print -eq 0 ]
then
[ -e “$1” ] || (echo “No such file or directory.” >&2 && return 2)
fi
last_name=$(get_new_name “$1” “${replace_char}” “${append_chars}”)
if [ $only_print -eq 1 ]
then
if [ $allow_recursion -eq 1 ]
then
echo “-p conflicts with -r.” >&2
print_help >&2
return 3
else
echo “$last_name”
fi
else
[ “$1” != “$last_name” ] && mv “$1” “$last_name”
if [ $allow_recursion -eq 1 ]
then
if [ ! -d “$last_name” ]
then
echo “$1”” was not a directory!” >&2
return 4
fi
(cd “$last_name”
ls | while read file
do
echo “$file”
if [ -d “$file” ]
then
main “$file”
else
allow_recursion=0
main “$file”
fi
done)

        fi
    fi
fi
return 0

}

while getopts “rvhsuoqx:a:c:p” var
do
case $var in
v)
print_version
exit 0
;;
h)
print_help
exit 0
;;
c)
replace_char=$OPTARG
if [ ${#OPTARG} -ne 1 ]
then
echo “-c must be followed by a char.” >&2
print_help
exit 1
fi
;;
a)
append_chars=$OPTARG
;;
q)
only_query=1
;;
p)
only_print=1
;;
o)
only_posix=1
;;
r)
allow_recursion=1
;;
u)
remove_dup_underscores=1
;;
s)
only_whitespace=1
;;
x)
start_with_dash=1
tmp=$OPTARG
;;
*)
echo “Bad options!” >&2
print_help
exit 1
esac
done

shift $(($OPTIND - 1))
if [ $start_with_dash -eq 0 ]
then
main “$@”
else
tmp=$(perl -e ‘$foo=”‘“$tmp”‘“;$bar=$foo;$foo=~s/^-*//;rename(“$bar”, “$foo”);print $foo;’)
main “$tmp”
fi
exit $?

[/bash]

用脚本解决C语言问题

vvoodyxiyoulinux上提出了一个C语言的问题,这个问题我之前见过,所以不想自己想了,想让计算机帮我完成。于是费了一番功夫写了个脚本帮我完成。(哎,老了,写代码慢了~~)

先贴“答案”,应该是总共8个结果(下面的9个中有两个明显是等价的):

int i,n=99;main(){for(i=0;i<n;i—){printf(“#”);}}
int i,
n=99;main(){for(i=0;i<n;i—){printf(“#”);}}
int i,n=99;main(){for(i=0;-i<n;i—){printf(“#”);}}
int i,n=99;main(){for(i=0;~i<n;i—){printf(“#”);}}
int i,n=99;main(){for(i=0;i<&n;i—){printf(“#”);}}
int i,n=99;main(){for(i=0;i<n;i—);{printf(“#”);}}
int*i,n=99;main(){for(i=0;i<n;i—){printf(“#”);}}
int i,n=99;main(){for(i=0;i+n;i—){printf(“#”);}}
int i,n=99;main(){for(i=0;i<n;n—){printf(“#”);}}

用时:

real 1m3.881s
user 0m44.097s
sys 0m17.436s

最让我吃惊的结果是那个溢出的,很巧妙,脚本运行到那里停了大约4,5秒钟的样子。

最后放脚本,包括两部分,一部分用bash完成,另一部分用Perl完成。其实完全用bash或者完全用Perl都是可行的,而且完全用Perl应该是最快的。

[bash]

!/bin/bash

TEST_VAR=’int i,n=99;main(){for(i=0;i<n;i—){printf("#");}}'

Generated by Python with

''.join(map(lambda x: '' if chr(x).isupper() else chr(x), range(32, 127)))

except '$','(',')', '{','}', '[', ']', '@', "`", """, "#", "'", "\", ' '

possible_chars='!'"%&*+,-./0123456789:;?^_abcdefghijklmnopqrstuvwxyz|~”
len=${#TEST_VAR}
num_chars=${#possible_chars}
tmpfile=$(mktemp /tmp/test-$$.XXXXXXX)
outfile=”/tmp/test_sh.out”

function is_valid_and_ok()
{
echo $1 > $tmpfile
if gcc -xc -c -o $outfile.o $tmpfile &>/dev/null;
then
gcc -o $outfile $outfile.o &>/dev/null
if [[ $? -ne 0 ]]; then
return 0
fi

    #nohup $outfile &gt; $resultfile 2&gt; /dev/null &amp;
    #sleep 1 &amp;&amp; killall $outfile 2&gt; /dev/null
    n=$(./get_run_result.pl $outfile)
    echo $n
    if [[ $n -eq 99 ]] || [[ $n -eq 100 ]] || [[ $n -eq 1 ]]
    then
        return 1
    fi
fi
return 0

}

main

i=8
while(($i $tmp” 1>&2
fi
i=$(($i+1))
done;

i=4
while (($i < $len - 14));
do
j=0
while(($j $tmp” 1>&2
fi
j=$(($j+1))
done
i=$(($i+1))
if (($i == 11)) ; then
i=17
fi
done;

i=0
while(($i<$len - 16));
do
j=0
while(($j $tmp” 1>&2
fi
j=$(($j+1))
done
i=$(($i+1))
if (($i == 11)) ; then
i=17
fi
done;
[/bash]
[perl]

!/usr/bin/perl -w

use strict;
use warnings;

my $n;
my $chars;

open(my $in_file, “$ARGV[0]|”) or exit 1;
$n = read($in_file, $chars, 101);
print “$nn”;
close $in_file;
[/perl]

Programming and Sex

[郑重警告:未满18岁的请自觉离开!]

有这么一句甚出名的话:”Software is like sex: It’s better when it’s free.” 今天才知道,原来是Linus说的。。。于是,solidot上有人由此推出:使用盗版像是被诱奸

在编程这方面,其实和sex相关的“名言”还挺多,整理如下:

Programming is like sex: one mistake and you’re providing support for a lifetime.”
(Michael Sinz)

“Documentation is like sex; when it’s good, it’s very, very good, and when it’s bad, it’s better than nothing.” (Dick Brandon)

“Saying that Java is nice because it works on all OSes is like saying that anal sex is nice because it works on all genders.”
(Alanna)

“How C++ is like teenage sex:
It is on everyone’s mind all the time.
Everyone talks about it all the time.
Everyone thinks everyone else is doing it.
Almost no one is really doing it.
The few who are doing it are: A. Doing it poorly. B. Sure it will be better next time. C. Not practising it safely.”

“UNIX is sexist
Type ‘man’ and help is given
woman is not found

[…]

UNIX is like sex
After it goes down on you
You have to ‘fsck’ it

[…]

UNIX is like sex
Root privleges are needed
To use lower ports” (Unix Haiku)

“Photoshop is like sex, you can actually tell who’s a professional!”

“Software is like sex; if you feel the need to pay for it you can always find someone willing to take your money.”

“Reading computer manuals without the hardware is as frustrating as reading sex manuals without the software.” — Arthur C. Clarke.

“Overclocking is like sex: if you smell burning, just slow down.”

参考:

101 Great Computer Programming Quotes
Programming Quotations
Computer programming quotes
Your Favorite Programming Quote
Quotations about Computer Programming
Famous and not so famous programming quotes

UTF-8版的strlen

这里看到这么一个计算UTF-8字符串长度的程序:

[c]
int my_strlen_utf8_c(char *s) {
int i = 0, j = 0;
while (s[i]) {
if ((s[i] & 0xc0) != 0x80) j++;
i++;
}
return j;
}
[/c]

不解。查wikipedia,得一表:
00000000 00000000 0zzzzzzz 0zzzzzzz
00000000 00000yyy yyzzzzzz 110yyyyy 10zzzzzz
00000000 xxxxyyyy yyzzzzzz 1110xxxx 10yyyyyy 10zzzzzz
000wwwxx xxxxyyyy yyzzzzzz 11110www 10xxxxxx 10yyyyyy 10zzzzzz

顿悟。

time为啥是关键字?

前面提到time其实是shell的一个关键字。

大家看了都奇怪为啥它非得是关键字?我今天仔细琢磨了一下,觉得有这么两个理由:

1. 为了让time也可以对cd之类的内置命令进行计时,比如:

$ time cd /

real 0m0.000s
user 0m0.000s
sys 0m0.000s

如果time是外部命令,或者shell内置命令的话,它根本就不好识别shell内置的cd,所以得是关键字。或者说只有是关键字才是这里最好的解决方案。

2. 为了重定向的考虑,比如:

$ time ls -l > /dev/null 2>&1

real 0m0.193s
user 0m0.005s
sys 0m0.005s

如果time是个命令,它的输出也会被重定向了,而它是关键字,所以它本身不会受影响。同理,管道也影响不了它:

$ ls -l | wc -l
25
$ time ls -l | wc -l
25

real 0m0.056s
user 0m0.006s
sys 0m0.007s

问题:那究竟如何重定向time自己的输出呢??

答:$ { time ls; } 2> /dev/null

参考:http://www.cs.tut.fi/~jarvi/tips/bash.html