May 26, 2010

tar打包之路径

一直都在用一个小脚本备份一些重要的东西到优盘上, 但是生成的目录结构我很不满意.

例如我要打包~/a, ~/b/c, ~/d/e三个目录, 以前我是:

cd ~
tar czf foobar.tgz a b/c d/e

这样解压后仍然会出来a, b/c, d/e这样的目录结构, 而我理想情况是解压后出来a, c, e三个目录, 也就是说我只想保留一层目录结构. 想解决这个还是得从研究tar的参数入手, -C这个更改目录的选项虽然以前也知道, 但当时小看它了, 今天试了一下, 竟然可以这样:

tar czf foobar.tgz -C ~ a -C ~/b c -C ~/d e

连续变更三次当前目录, 这样tar包里的结构就是a, c, e了. 注意路径写全, 因为后一次-C默认的路径依赖于前一次-C.

—————————胡乱感慨的分割线—————————

  1. C和*nix果然是最符合Geek变态思维的玩具.

  2. 这个世界最伟大的秩序就是: 除了不让的, 剩下的都可以.


May 24, 2010

My home

无聊, 贴张卫星图片, 点击放大. 我家就隐藏在其中一个居民楼里, 距离海边直线距离286m. 然后, 赋诗一首:

The sea rocks have a green moss.
The pine rocks have red berries.
I have memories of you.

Speak to me of how you miss me.
Tell me the hours go long and slow.

Speak to me of the drag on your heart,
The iron drag of the long days.

I know hours empty as a beggar’s tin cup on a rainy day, empty as a soldier’s sleeve with an arm lost.

Speak to me …

呵呵, 你猜对了, 不是我写的, Home Thoughts by Carl Sandburg.


May 23, 2010

terminal, bash和screen的配合

首先, 术业有专攻, 命令行和图形界面不是水火不容, 我都用的很多, 少了哪个也不行.

其中关于命令行, 我基本上不进console, 毕竟分辨率, 字体, 中文什么的都比较恼火. 自己电脑就是xterm+screen+bash, 办公室就是用putty+ssh+screen+bash. 想要有一个舒服的使用环境, 就得把这几个家伙配合好.

想要实现一个什么效果呢? 最好运行screen的时候, 每个window的title为路径或者当前运行的程序, xterm的title为screen: 加上当前window的title; 而单独运行xterm的时候, xterm的title为user@host: path. 比较晕是吧? 看我的效果图:

下面是一些关键的配置, 其它部分在此: https://github.com/adam8157/dotfiles

bash的.bashrc:

#screen and xterm's dynamic title
case $TERM in
	xterm*)
		# Set xterm's title
		TITLEBAR='\[\e]0;\u@\h:\w\a\]'
		PS1="${TITLEBAR}${PS1}"
		;;
	screen*)
		# Use path as title
		PATHTITLE='\[\ek\W\e\\\]'
		# Use program name as title
		PROGRAMTITLE='\[\ek\e\\\]'
		PS1="${PROGRAMTITLE}${PATHTITLE}${PS1}"
		;;
	*)
		;;
esac

screen的.screenrc:

# Caption line
caption always "%{= R}[ %{=b b}%-w%{=rb db}%>%n %t%{-}%+w%{-b}%< %=%{R}][%{M}%D %M %d %{G}%c%{R}]"

# Dynamic title
shelltitle '$ |bash'

# Set xterm's title
hardstatus string "screen: %t"

bash中主要是设置了几个escape sequences, 具体都是screen和xterm自己定义的, 把它们塞到PS1中, 看不到的同时还能给screen和xterm发信号, 让它们改对应的title.

screen中状态栏选用Caption而不用Hardstatus实现, 是因为要把xterm的title伪装成Hardstatus, 这个可能算个历史遗留问题, putty也是这么处理的. shelltitle就是实现将所运行程序的名称作为screen的title的功能, 默认bash,, 有命令的话就是”$ “后面的第一个字符段.


May 23, 2010

Bash下实现alias补全

我是个懒人. 很多较长的命令, 比如apt的搜索, 安装, 卸载和清理都让我搞成了alias. 但简化输入的同时也带来个问题: 完整的命令配合bash_completion可以实现自动补全包名称, 现在用alias却得小心翼翼地输入. 这怎么行? 必须解决掉.

之前的补全是配合bash_completion实现的, 所以很明显, alias补全也要从研究bash_completion入手. 分析/etc/bash_completion可以看出, bash_completion实现了很多用来获得补全项的函数, 然后用bash的内置命令complete将命令和函数对应起来以实现补全. 所以, 解决方案一目了然, 将函数提取出来和我自己定义的alias对应起来就好.

以下是.bashrc中的配置

#completion
if [ -f /etc/bash]_completion ]; then
	. /etc/bash_completion
fi

_show_installed()
{
	local cur
	COMPREPLY=()
	cur=`_get_cword`
	COMPREPLY=( $( _comp_dpkg_installed_packages $cur ) )
	return 0
}

_show_all()
{
	local cur
	COMPREPLY=()
	cur=`_get_cword`
	COMPREPLY=( $( apt-cache pkgnames $cur 2> /dev/null ) )
	return 0
}

complete -F _show_all $default ai aw
complete -F _show_installed $default ap

#alias
alias ai='sudo apt-get install'
alias ap='sudo apt-get purge'
alias aw='apt-cache show'

解释一下: _show_installed()用来获得已经安装的包名称, _show_all()用来获得源里所有的包名称. complete命令用以建立对应关系. 最后是我需要补全的alias. 很简单吧, 如果你有其它需要补全的alias, 函数都在/etc/bash_completion中, 提出来照着改就是了.

PS: zsh的补全功能很强, 不用这么折腾, 有兴趣的朋友也可以试一下.


May 21, 2010

mutt强大的hook规则

以前也知道mutt可以利用hook对特定情况特殊处理, 但是一直没需求, 直到上个月订阅了中文内核邮件列表.

这个列表会在邮件最后放一段文字, 所以发PGP签名的邮件就会失败. 很明显, 签名就是保护邮件正文不被修改的, 但这个邮件列表就是要改你的邮件正文, 自然不行. 问题在于我喜欢设置为默认自动签名, 所以每次给这个邮件列表发信就得手动改PGP为Clear, 哪次忘了改? 好吧, 你的回复就消失无踪了.

好在强大的mutt总是能满足我的各种需求, 想要默认签名, 但是发给中文内核列表的不签? 没问题.

下面是配置:

send-hook ~A "set pgp_autosign"
send-hook "~C linux-kernel@zh-kernel.org" "set nopgp_autosign"

解释一下, 第一句设置默认情况为自动签名, 第二句匹配所有在To:或者Cc:中包含linux-kernel@zh-kernel.org的发送或者回复邮件, 此时取消自动签名.

===============无聊的分割线===============

刚写到这里, 又想到了hook的另一个应用: 我的mutt是配合内置imap的, 绑定了d为移动到Gmail的Trash, 但是如果你要删除Trash中的邮件, 就得再绑定一个键(例如D)为删除, 很麻烦. 理想情况是在别的文件夹中绑定d为移动到Trash, Trash中绑定d为删除. 这个实现起来依然轻松, 可以利用folder-hook.

直接上配置:

folder-hook . 'macro index,pager d "=[Gmail]/Trash" "Mark message as trash"'
folder-hook 'Trash' 'bind index,pager d delete-message'

mutt实在是强大的过份, 想怎么玩怎么玩, 向mutt致敬!


May 18, 2010

赛季结束--冠军!

Yeah, 99分创历史记录夺冠, 巴萨是最棒的!

祝愿巴萨下赛季多线开花, 也祝大家身体健健康康.

…不多说了, 贴张图, 补觉去…


May 15, 2010

vim中单个文件多个备份的实现

经常备份绝对是个好习惯, 一个救过我很多次的好习惯.

编辑器我只用vim, vim也有自动备份的功能, 但是存在一个问题: vim默认一个文件只对应一个备份, 所以你只能回退一次, 再之前的版本就看不到了. 这是不够用的, 也是不可容忍的, 状况随时会发生, 我们需要对一个文件保存多个备份, 最好每次修改就备份一次, 而且都保存下来.

当然, vim有基于git等版本控制系统的备份插件, 个人觉得有点小题大做了, 有点麻烦, 而且使得vim的配置和插件目录显得臃肿, 跨平台能力也不强.

我的方案是靠vim自己来实现, 每次修改就保存一个备份, 备份的文件名中包含修改的时间以方便识别. 下面是我的配置: 1, 打开备份功能; 2, 设置备份目录; 3, 每次写缓冲之前设置备份文件的后缀格式为当前的”.月-日-时-分”. 默认后缀是”~”, 不存在变化, 所以只对应一个备份. 像我这样配置后, 最后的效果会类似这样: foo.c.5-15-17-30, foo.c.5-16-01-25.

" Turn backup on
set backup

" Set backup directory
set backupdir=$HOME/.vim/backup

" Keep more backups for one file
autocmd BufWritePre * let &backupext = strftime(".%m-%d-%H-%M")

May 12, 2010

shell脚本中的空格

一直不太理解shell脚本中为什么那么多空格不可省略(比如[和]), 又有那么多空格不可以有(比如=), shell脚本为什么对格式要求这么高?

查了好多, 基本上都在总结哪里必须空格, 哪里必须不带空格. 明显, 这不是问题的本质所在. 虽然我脚本写的不多, 但是我总感觉shell脚本很优雅. 一门优雅的语言, 如shell和c, 应该是原理决定规则, 而不是像C++一样, 规则去决定实现. 不要激动, C++之父Bjarne Stroustrup就说过, 他实际上是创造了一堆他心目中的强大的面向对象语言的理论规则, 并且他不认为有人能够完全的理解C++.

扯远了, 那么关于shell的空格要求是个什么原理呢? 其实, 和你表观看到的不一样, 有些shell表达式其实是命令风格的函数, 例如”[”, 实际上, 它是个和test差不多的函数, 它后面跟的, 包括”]”, 都只是它的参数, shell中输入命令的时候要靠空格来区分各个参数, 这里也是一样. “=”两边不能有空格同样也是因为它的命令风格要对这些进行区分的要求. 虽然很诡异, 但是也可以理解, 毕竟shell脚本就是逐行运行的命令.

有些知识的积累就该是这样, 去理解而不是去总结. 就像c的数组下标为什么从0开始, 只要明白了a[i]同等于*(a+i), 一切都是那么自然.


May 10, 2010

可怜的鸭子妈妈

贴个图, 调剂下. 全技术文章显得博客太单调了.

ggarlic同学的Google Reader分享看到的. 好可怜, 但是也挺可爱的, 相信拍照的人已经出手相救了…


May 10, 2010

清理Debian内核和头文件的脚本

改自bones7456清理ubuntu内核的脚本, 原文地址在此: http://li2z.cn/2010/02/28/clean_ubuntu_kernel/.

Debian和Ubuntu的包命名方式稍有不同, 不能直接用, 改了下, 再就是避免删除元包, 增加了响应输入的功能.

#!/bin/sh

CURRENT="`uname -r | awk -F"-" '{print $1"-"$2}'`"
HEADERS=""
IMAGES=""
ARCH="amd64"

for HEADER in `dpkg --get-selections | grep ^linux-headers | grep -v "${ARCH}" | awk -F"-" '{print $3"-"$4}'`
do
	if [[ "$CURRENT" < "$HEADER" ]]
	then
		echo "The running kernel is not the newest. $CURRENT < $HEADER"
		exit 1
	else
		[[ "$CURRENT" != "$HEADER" ]] && {
			HEADERS="${HEADERS} linux-headers-${HEADER}-${ARCH} linux-headers-${HEADER}-${ARCH}-common"
			IMAGE="`dpkg --get-selections | grep ^linux-image | grep "${HEADER}" | awk '{print $1}'`"
			IMAGES="${IMAGES} $IMAGE"
		}
	fi
done

if [[ -z "$HEADERS" ]]
then
	echo "There is no old kernel or headers need to be cleaned."
	exit 0
fi

echo
echo ------------------------------------
echo "$IMAGES $HEADERS"
echo ------------------------------------
echo

read -p "Really wanna remove these packages? [y/N] " REPLY

case $REPLY in
	y | Y )
		sh -c "sudo apt-get purge $IMAGES $HEADERS" ;;
	* )
		echo "The operation is cancelled." ;;
esac