Archives

比较两个文件的相同

恐怕地球人都知道比较两个文件的不同得用diff(1),现在有个问题是:如何找出两个文件的相同之处?

经过简单查找,我发现可以用comm(1)。可是看了一下它的man我们就发现,不妥,它没diff(1)那么简单。首先它的选项有些特别;其次,很重要的是,它要求文件必须是排好序的!这时候恐怕我们就得想,哎,又得用bash脚本了。不行,那不符合hack精神,我就不信在一行里整不出它来!于是下面的一行就出来乐:

% comm -1 -2 <(cat 1.txt | sort) <(cat 2.txt |sort)

或者更干脆:

% comm -1 -2 <(sort 1.txt) <(sort 2.txt)

zsh和bash都可以用的。可见,shell语法是够博大精深的……

课后作业:不看源代码,猜猜上面的技巧大体上是怎么实现的?(想知道“答案”的拖住最后一行。;-)

提示:把comm换成diff看看,是不是用的/proc/self?

跨行搜索脚本:mgrep

写了做跨行搜索的脚本,用Perl。我知道也可以在bash里用sed,但我认为用那的话灵活性就不如用Perl了,因为我还要给它扩展各种我需要的功能。

闲话少说,上代码!

[perl]

!/usr/bin/perl -w

From: http://www.kclug.org/pipermail/kclug/2005-June/028106.html

Hacked by WANG Cong.

Copyright (C) 2008, WANG Cong

Usage

mgrep.pl -s -e [—nonum]

use strict;
use warnings;
use Getopt::Long;

my($start, $end, $file);
my $num = 1;

my $allow_nest = 0;

my $allow_recursion = 0;
my $allow_binary = 0;

GetOptions(
“start=s” => $start,
“end=s” => $end,
“nonum” => sub { $num = 0; },
“recursion” => sub { $allow_recursion = 1 },
“binary” => sub { $allow_binary = 1 }

“nest” => sub { $allow_nest = 1; }

);

$file = shift;

die “$start and $end options are both required. And so is the file name.”
unless ($start && $end && $file);

my $start_reg = eval {qr/$start/};
die “The start pattern is not correct!” if $@;
my $end_reg = eval {qr/$end/};
die “The end pattern is not correct!” if $@;

## sub

sub mgrep_file
{
my @matches;

#my @nest_stack;
my $matching; #boolean
my $added = 0; #boolean
my $linecount = 0;
my $file = shift;

open( IN, $file ) or return undef;

while( my $line = readline *IN)
{
$linecount++;
if( $line =~ /($start_reg.*)/ )
{
    $matching = 1;
    if(!$added){
    push @matches, "==", $file, "==n";
    $added = 1;
    }
    if ($num) {
    push( @matches, $linecount . ": " . $1 . "n" );
    }else{
    push( @matches, $1 . "n" );
    }

    if( $1 =~ /$end_reg/){ $matching = 0; }
    next;
}

if( $matching )
{
    if( $line =~ /(.*$end_reg)/ )
    {
    if ($num){
        push( @matches, $linecount . ": " . $1 . "n" );
    }else{
        push( @matches, $1 . "n" );
    }
    $matching = 0;
    }
    else
    {
    if ($num){
        push( @matches, $linecount . ": " . $line );
    }else{
        push( @matches, $line);
    }
    }
}
}
close IN;
return @matches;

}

sub mgrep_dir
{
my $dir = shift;
my @file_list;
my @match_list = ();
my $ret;
my @lret;

opendir(IN_DIR, $dir) || return undef;
@file_list = grep { $_ ne '.' and $_ ne '..' } readdir IN_DIR;

if (@file_list){
for my $one (@file_list) {
    my $fname = "$dir/$one";
    if (-f $fname){
    next if(!-T $fname &amp;&amp; !$allow_binary);
    $ret = mgrep_file($fname);
    next unless @$ret;
    push @match_list, @$ret;
    }
    if ($allow_recursion &amp;&amp; -d $fname){
    @lret = mgrep_dir($fname);
    next unless @lret;
    push @match_list, @lret;
    }
}
}

closedir IN_DIR;
return @match_list;

}

##### main

while ($file)
{
my $ret;
my @list;

if (-f $file){
next if(!-T $file &amp;&amp; !$allow_binary);
$ret = mgrep_file($file);
print @$ret;
}
elsif (-d $file){
@list = mgrep_dir($file);
print @list;
}else{
die "$file is not existed!";
}

$file = shift;

}

[/perl]

Python和Perl都没消除自指?

[perl]

!/usr/bin/perl

use strict;
use warnings;

my @foo = qw(foo1 foo2);
my @bar = qw(bar1 bar2);

push @foo, @bar;
push @bar, @foo;

print @{$foo[2][2][2][2][2][2][2][2][2][2]}, “n”

please add more if you want

[/perl]

[python]

!/usr/bin/env python

foo = [‘foo1’, ‘foo2’]
bar = [‘bar1’, ‘bar2’]

foo.append(bar)
bar.append(foo)

print foo[2][2][2][2][2][2][2]

please add more if you want

[/python]

C语言显然不会出现这种情况,因为要用指针的话这两种指针显然不是一个层次上的,必须通过强制转化。而Python和Perl之所以都会出现这种情况是因为他们的的list都太NB了,啥都能放,包括它本身!!

或者是我最近看《集异璧》看多了??

发布Qbak项目

Qbak 是 “Quick Backup” 的缩写。Qbak 是 Linux 上使用的备份工具,目前只有 命令行界面。但我会考虑以后为它加入图形界面。

Qbak 的使用和配置非常简单,它读取~/.qbakrc作为配置文件,并把里面指定的目录作为“输出目录”,即存放备份文件归档的目录。建议设置为”~/.qbak”。 关于配置项请看源代码中的示例配置文件。

Qbak 使用了一种半“堆栈式”的管理方式,即通过push和pull来加入和导出备份文件。Qbak 的接口有点像git,通过其子命令来完成各种功能。想了解更多请去项目主页

现在 Qbak 还不成熟,有一些小bug,还有许多待完善的地方。Qbak 是用bash脚本写成的,欢迎有兴趣的同学加入!!

下载地址:
http://wangcong.org/projects/qbak-0.1.tar.gz

Qbak 项目主页:
http://wangcong.org/projects/qbak.html

这张图真的还是假的?

今天王冬同学发给我的。哪位如果用搜狗输入法能否帮忙确认一下真假?怎么看也不像PS的啊!(真要是话的说明那人PS水平不低~!)不知道是哪位高人做的?或者是西邮在搜狐有卧底?

assembler的一个hack

如果编译一个relocatable目标文件,你通过反汇编不难发现所有的call指令竟然都是同一个机器码!即:e8 fc ff ff ff。

我们知道,不同的函数有不同的入口,可这里怎么都会用这么一个奇怪的地址呢?

翻一下Intel指令手册,我们很容易就能在3-104 Vol. 2A中找到call指令的各种编码方式。根据这里的opcode为e8我们不难确定这个call是用的下面这种方式:

E8 cd CALL rel32

这就告诉我们,opcode后面其实是一个32位相当的相对地址。如果你肯在往后翻几页的话,就会发现这种方式的执行过程,如下:

tempEIP ←EIP +DEST; ( DEST is rel32 )
IF tempEIP is not within code segment limit THEN #GP(0); FI;
IF stack not large enough for a 4-byte return address
THEN #SS(0); FI;
Push(EIP);
EIP ←tempEIP;

也就是说会用当前的EIP加上那个相对地址作为最终的call转移地址。别慌,还没完,我们继续。

我们知道,relocatable目标文件的符号地址都是不能使用的,因为还没经过linker转化。linker转化后的地址才是最终的地址,也就是说从上面那个地址到最后的地址还有一段过程,由ld来完成。

我们知道,这里正确的地址应该是该符号实际的地址与这个call地址之间的偏移。而这等于这个函数的实际入口与.text section之间的偏移减去这个符号相对.text的偏移!后者由ELF格式直接给出,而前者也很容易计算。

到这里你会发现,不对,我们那个fc ff ff ff还没用上!是,因为我们前面忽视了很重要的一点,EIP是指向下一条指令的而不是当前这条的地址!也就是说,我们前面的结果需要修正!具体说是需要修正一个 -0x4。再看一下fc ff ff ff,不正是-4么?!(x86是little endian!)对,正是这个由assembler故意安排的-4修正了我们call指令!

当然了,如果符号不是一个函数地址,而是一个全局变量的地址,这就不需要修正,相应的relocatable文件里就是0。

不得不说这是一个很聪明的hack!

还有,如果你看最后executable文件,你可能又困惑了,call后面的修正似乎和上面不一样。举个例子:

80483ba:       e8 18 00 00 00          call   80483d7 <foo>

0x80483ba+0x18明显不是0x80483d7。嘿嘿,这里就不一样了,因为这里需要修正的是-0x5。差的那个会是啥呢?猜猜吧?(用鼠标拖住最后一行看答案。;-)

就是那个e8啊!

关于Linux的syslog

内核中printk发出的消息是这样传递到用户空间的。

内核留给用户空间的接口是syslog(2),glibc对它进行了包装,于是就有了klogctl(3)和syslog(3)。

先由klogd通过klogctl(3)搜集内核消息(见sysklogd/klogd.c),并通过自己实现的syslog()(注意:和syslog(2),syslog(3)都不一样)传递给 syslogd。syslogd根据/etc/syslog.conf的配置情况进行记录。syslogd提供的是一个统一的方式,它不单单记录内核的消息,还包括其它服务器进程的消息,比如Apache,vsftp。

klogd和syslogd用的syslog()的实现用的是Unix domain socket,如果你看源代码(sysklogd/syslog.c)的话很容易就知道这一点。所以,所谓的syslog()/openlog()/closelog()等接口无非就是对这个socket操作的一个包装。这也就清楚了klogd和syslogd之间的通信方式。

根据syslogd的源代码(sysklogd/syslogd.c)来看,记录日志也可以是发送到远程的,而那个socket就是用的inet socket了。

多说一句,其实内核给出的oops只是一堆地址,而把地址和相应的符号对应起来也是由klogd完成的。sysklogd/ksym.c里有搜索System.map的过程,搜索的顺序是:
“/boot/System.map”
“/System.map”
“/usr/src/linux/System.map”

这就是最好的证据。一些细心的人或许还会奇怪,我们不是还有/proc/kallsyms么?怎么不用它?我知道的解释是:为了照顾那些没有/proc文件系统的Linux系统。;-)

我们再来看syslog(3),即glibc实现的syslog()。看一下glibc中的源代码,我们就会发现glibc中的syslog和sysklogd自己的实现类似,也是用unix domain socket来实现的。具体可以看:glibc/misc/syslog.c。

这两种实现之所以都能够传递给syslogd,是因为它们的unix domain socket都是用的/dev/log这个文件。

而我们最常用的查看内核消息的命令dmesg(1)和上面两位守候进程没太大关系,它是直接通过glibc的klogctl(3)函数来读取内核消息的。详细见:util-linux-2.13-pre7/sys-utils/dmesg.c。

如果想了解更多关于syslogd的东西,可以参考这篇文章

想了解内核的中对syslog(2)的实现,可以参考内核源代码:kernel/printk.c。

又hack了一个shell脚本

正如董溥同学给我留的言,coolcode插件不支持C语言,这是事实。而且用coolcode插件往wordpress里贴代码也是不很爽~!

于是乎,我就hack了一个脚本,调用vim来生成html,然后往博客里贴时复制里面的html代码就是了。恩,我知道emacs有个htmlize插件,可惜它生成的html是CSS的,貌似wordpress不支持直接贴那玩意(未经验证)。我用它更新了一下前面一篇文章中的代码,效果还不错,代码如下:

#!/bin/bash #Copyright(C) 2008, WANG Cong #GPLv3 applies. if (($# != 1)) ; then

        echo "Bad usage!"

        exit 1

fi if [ ! -f $1 ] ; then

        echo "$1: No such file!"

        exit 2

fi

vim -n -c :so $VIMRUNTIME/syntax/2html.vim -c ":w $1.html" 

        -c ":qa" $1  > /dev/null 2> /dev/null

exit 0

生活的意义在于折腾

有句话说得好:“生,容易。活,容易。生活,不容易!”

生活很多时候都会是很平淡的,时间长了就会觉得无趣。这时候别着急,你得学着折腾,没事折腾点事来做。这样的生活才会精彩! 折腾够了这个,歇歇再折腾那个。让生活不再那么无聊!

有时候,生活也会不那么顺心,别着急,把它当成生活给你提供的“折腾”,这样省得你再自己折腾!折腾过去了再回头看看,生活不还是得继续么?还是得重回无趣?

然后就接着折腾……

生命不息,折腾不止!

聪明的墙

“这些墙很有趣。刚入狱的时候,你痛恨周围的高墙;慢慢地,你习惯了生活在其中;最终你会发现自己不得不依靠它而生存。这就叫体制化。 ”——《肖申克的救赎》
我发现,墙忒有才了!

首先,墙懂得正则表达式!它对”^wiki“域名的网站一概过滤,而”wiki$”却没事。

其次,墙知道今天是愚人节!为了幽大家一默,今天破例可以访问blogspot和en.wikipedia.org!

如果第2条猜测不成立,即明天仍可以访问,那就说明墙懂得支持闹运会。8月24之后应该就会关闭!

如果最后一点仍不成立,那说明你在做梦!

哎,墙啊,你这玩笑开得太突然了!突然没了你俺受不了啊!俺这心挖凉挖凉的!

(今天距离北京闹运会结束还有145天。。。)