阅读提示:

本文已过时。

如果你有多台不同的机器的话,你也许希望能在不同的机器上都使用同样的用户配置,例如窗口样式、Firefox 扩展、应用程序偏好设置等。虽然在 Linux 所有用户配置都在 $HOME 目录下,只要完整同步 $HOME 目录,就能把所有用户配置同步到另一台机器上,但完整同步整个 $HOME 目录却不是每个人都能负担的开销,也不是所有人都愿意像滇狐这样把 $HOME 放在移动硬盘里面随身携带。因此,我们需要打造一套便携式的用户配置文件,让我们在不同机器之间迁移时更加方便灵活。

注意:本文按照思考问题的思路顺序来介绍打造方法,因此接下来的部分中会提到许多半成品脚本,在其后的章节里可能会多次修改这些脚本。因此在你确认这些脚本是最终版本之前,没有必要把脚本抄到你自己的系统中试验。

1 收集需要同步的配置文件

要想在不同机器中使用同样的配置,首先能够想到的就是同步我们家目录下点开头的文件和文件夹。但是,这些点开头的文件和文件夹并不是都需要被同步的,例如除非你是专业美术设计人员,否则你肯定不会愿意同步 ~/.gimp* 文件夹。另外,同步 Firefox 本地缓存、KDE 图标预览缓存等,也都是很让人不愉快的经历。因此,我们需要将需要同步的内容和不需要同步的内容区分开来。实现这点很容易,因为 Linux 支持符号链接!我们只需要建立一个文件夹,然后将需同步的内容都移动到这个文件夹下,然后在原始位置创建指向这些移动后的文件的符号链接就可以了。

于是,滇狐创建了 ~/config,然后准备在这个文件夹下存放需要同步的配置文件。为了在 Konqueror 下编辑配置文件更加方便,滇狐把 ~/.filename 移动到 ~/config 下后,顺便改名为 dot.filename,这样,该文件就不再是隐藏文件了,可以在 Konqueror 下直接双击打开。为了让配置文件的收集更加便利,滇狐创建了以下脚本:

#!/bin/sh
# Filename: gather.sh

if (test $# = 0) then
    echo Usage: gather.sh filename
else
    for file in "$@"
    do
        if [ ! -e "dot`basename $file`" ]; then
            cp -rfa "$file" "dot`basename $file`"
        fi
    done
fi

打开 Konsole,切换到 ~/config 目录,然后运行 ./gather.sh ~/.vimrc,就可以把 ~/.vimrc 复制到 ~/config 中,并改名为 dot.vimrc。滇狐使用这样的方法收集了以下文件:

dot.bash_logout     dot.bash_profile    dot.bashrc          dot.deplate
dot.dir_colors      dot.fonts.conf      dot.gaim            dot.gconf
dot.gconfd          dot.google          dot.gtkrc           dot.gtkrc-2.0
dot.gtkrc-2.0-kde   dot.gtkrc.mine      dot.kde             dot.kderc
dot.lftprc          dot.mozilla         dot.mutt_alias      dot.muttrc
dot.pyinput         dot.qt              dot.scim            dot.screenrc
dot.themes          dot.vim             dot.vimrc           dot.xinput.d
dot.Xresources      dot.Xsession

收集了需要同步的文件后,当我们把 ~/config 文件夹整个复制到另外一台机器上后,需要把 $HOME 底下的那些文件或文件夹删了,改为指向 ~/config 中的相应文件或文件夹的符号链接。我们再创建一个简单的脚本来做这件事情:

#!/bin/sh
# Filename: settings.sh

for file in dot.*
do
    filename=${file/dot/}
    if [ -e ~/$filename ]; then
        if [ ! -L ~/$filename ]; then
            echo Trying to remove ~/$filename...
            rm -rf ~/$filename
            echo Linking $file to ~/$filename...
            ln -s `pwd`/$file ~/$filename
        fi
    else
        echo Linking $file to ~/$filename...
        ln -s `pwd`/$file ~/$filename
    fi
done

2 减少同步内容

现在我们已经拥有一个简单的、可在不同机器间同步的配置文件包了,但是,在这些配置文件包中,还是存在许多不需要被同步的内容,例如 Firefox 本地缓存等。解决这个问题很简单,还是使用同样的思路,将这些不需要同步的内容再创建一次符号链接,指到 ~/config 之外就行了。

2.1 Gaim

滇狐不是那种特别热衷于收集聊天记录的人,因此滇狐理所当然地将 Gaim 中的聊天记录、MSN 图标、QQ Show 等信息挪到同步目录之外。因此,滇狐在 ~/config/dot.gaim 中创建了以下符号链接:

icons -> ~/document/gaim/icons
logs -> ~/document/gaim/logs
qqshow -> ~/document/gaim/qqshow
smileys -> ~/document/gaim/smileys

为了方便在新的环境中创建这几个文件夹,滇狐写了这个简单的脚本,存为 ~/config/dot.gaim/settings.sh

#!/bin/sh

mkdir -p ~/document/gaim/icons
mkdir -p ~/document/gaim/logs
mkdir -p ~/document/gaim/qqshow
mkdir -p ~/document/gaim/smileys

2.2 KDE

KDE 的配置文件非常乖,一般守规矩的程序都会把用户偏好设置存放到.kde/share/config 下,把用户使用中生成的数据存放到.kde/share/apps 下,这给我们排除不需要同步的数据提供了极大便利,因为就滇狐个人而言,很希望能方便地同步偏好设置,但不大希望同步使用中生成的数据,因此我们直接把 ~/config/dot.kde/share/apps 移动到 ~/tmp 下,然后原地创建一个符号链接即可。

和前面类似,我们依然创建一个 ~/config/dot.kde/settings.sh,方便在新的环境中创建所需的文件夹:

#!/bin/sh

mkdir -p ~/tmp/apps

2.3 Firefox

和 KDE 的乖比起来,Firefox 那可是相当不乖。看一下它的配置文件所在的目录 ~/config/dot.mozilla/firefox/edward.default (在你机器上一定不是这样的路径,根据你的实际情况修改),里面需要同步的文件和不需要同步的文件犬牙交错,分离起来很困难。经过一番搜索,滇狐决定把这些文件或文件夹挪到 ~/tmp/mozilla 中:

bookmarkbackups     Cache               Cache.Trash         history.dat
urlclassifier2.sqlite

Firefox 对 history.dat 的格式检测非常严格,如果 history.dat 的格式不能满足它的要求,它会把 history.dat 整个删掉然后重新创建一个。这就意味着,即使我们为 history.dat 创建了符号链接并且把它指到~/tmp/mozilla 下,如果 ~/tmp/mozilla 下没有一个格式完全合法的 history.dat 的话,Firefox 会删除符号链接并创建一个非符号链接的 history.dat,这样我们下次向另一台机器同步的时候,history.dat 也就不得不参与同步了。为了解决这个问题,滇狐采取了这样的方法:

  1. 关闭 Firefox;
  2. 删除 history.dat
  3. 打开 Firefox, 此时会自动创建一个没有任何条目的 history.dat, 大概 376 字节;
  4. history.dat 复制到 ~/config/dot.mozilla 下备用;
  5. 到一台新机器架设工作环境时, 先将 ~/config/dot.mozilla/history.dat 复制到 ~/tmp/mozilla 下, 这样可以保证 Firefox 配置文件夹下的那个 history.dat 符号链接指向的文件有合法的格式。

相比之下,urlclassifier2.sqlite 文件可就乖得多了,即使目标文件格式不合法,它也只会重写目标文件,而不会删除符号链接重建,因此我们只需要简单地在 ~/tmp/mozilla 下创建一个空文件给它链接就好。

另外还有两个最可恶的文件 XPC.mfaslXUL.mfasl,它们都不是滇狐心目中的需要被同步的内容,却不但体积庞大,而且会不定期删除重建,使滇狐完全无法使用符号链接机制把它们从需同步内容中剥离出来,只好忍了。实在不行每次同步之前把它们两个都删一遍就好。

除了 Firefox 外,古老的 Mozilla 也把配置文件放在 ~/.mozilla/default 中。滇狐没有兴趣同步 Mozilla 的配置文件,所以把 ~/.mozilla/default 整个指到 ~/tmp/mozilla/default 就好。

为了自动实现上面说的一系列创建文件夹、复制 history.dat 等内容,我们编写一个简单的脚本存放到 ~/config/dot.mozilla/settings.sh 中。

#!/bin/sh

mkdir -p ~/tmp/mozilla/default
mkdir -p ~/tmp/mozilla/Cache
mkdir -p ~/tmp/mozilla/Cache.Trash
mkdir -p ~/tmp/mozilla/bookmarkbackups

touch ~/tmp/mozilla/urlclassifier2.sqlite

cp history.dat ~/tmp/mozilla/history.dat

2.4 Vim

和前面那些大批由程序自动生成的配置文件相比,Vim 的配置处理起来可容易多了,因为所有的配置文件都是用户自己编写或下载的,用户对于自己需要同步的文件非常清楚。

根据个人习惯,滇狐喜欢在 ~/.vim/plugin 下存放一些在 Vim 中常用的脚本或二进制应用程序,到了一台新的机器时,把这些程序直接 ln -s~/bin 中就可直接使用。和前面类似,滇狐在这里也提供了一个 settings.sh

#!/bin/sh

mkdir -p ~/bin
ln -f -s ~/.vim/plugin/clewn ~/bin/clewn
ln -f -s ~/.vim/plugin/gvim-latex ~/bin/gvim-latex
ln -f -s ~/.vim/plugin/omnictags ~/bin/omnictags
ln -f -s ~/.vim/plugin/start.kde ~/bin/start

其中 clewn 下载自 http://clewn.sourceforge.net,其余几个很简单的脚本内容如下:

#!/bin/sh
# Filename: gvim-latex

if [ $# -lt 1 ]; then
    echo Usage: gvim-latex filename
else
    for i in "$@"
    do
        gvim --servername latex-suite --remote-silent "$i"
    done
fi
#!/bin/sh
# Filename: omnictags

ctags --c++-kinds=+p --fields=+iaS --extra=+q "$@"
#!/bin/sh
# Filename: start.kde

if (test $# = 0) then
    echo Usage: start filename
else
    while (test $# -gt 0)
    do
        kfmclient exec "$1"
        shift
    done
fi

3 增加同步内容

在许多情况下,我们会发现,在一个配置文件目录中,大部分内容我们不希望同步,但有少部分内容我们希望把它同步起来。此时使用前面的“收集整个配置文件目录”后再“减少无须同步的内容”来进行处理的话显得非常繁琐,因此我们改用另一种思路,就是整个目录不移入 ~/config,将需同步的内容放入 ~/config 后再在外面创建符号链接指到~/config 中去。

3.1 applications

在 KDE 中,~/.local/share/applications 里存放了我们自定义的开始菜单图标、文件打开方式等信息,以一系列 .desktop 文件的形式存放。但 ~/.local 中却有大量别的我们不希望同步的内容,如回收站等。因此滇狐决定,将 ~/.local/share/applications/*.desktop 收集到 ~/config/applications 下,然后在原始位置创建符号链接指过来。

为了方便在新的机器上创建符号链接,我们编写一个这样的脚本:

#!/bin/sh
# Filename: settings.sh

mkdir -p ~/.local/share/applications

for file in *.desktop
do
    ln -sf `pwd`/$file \
        ~/.local/share/applications/$file
done

3.2 bin

也许你日常工作中会编写一些常用的小脚本,希望在不同机器中都可以使用。这样的话不妨把你编写的脚本都放到 ~/config/bin 中,然后在 ~/config/bin 中创建一个这样的脚本 settings.sh

#!/bin/sh

mkdir -p ~/bin

for file in *
do
    if [ "$file" != settings.sh ]; then
        ln -fs `pwd`/"$file" ~/bin/"$file"
    fi
done

3.3 face

KDE 和 GNOME 都允许定义用户自定义头像,但 KDE 是 ~/.face.icon,GNOME 是 ~/.face。没关系,反正我可以使用硬链接把它们都链接到同一个文件上。在 ~/config/face 中创建一张 png 图片作为头像,命名为 face.png,然后创建一个脚本,名叫 settings.sh

#!/bin/sh

ln face.png ~/.face
ln face.png ~/.face.icon
chmod 644 ~/.face
chmod 644 ~/.face.icon

3.4 QTerm

QTerm 文件夹下有大量不需要同步的内容,如图片浏览时留下的 Cache 等等。需要同步的只有两个文件:address.cfg——QTerm 地址簿;qterm.cfg——用户偏好设置。

将这两个文件收集到 ~/config/qterm 中,然后创建一个脚本 settings.sh,内容如下:

#!/bin/sh

mkdir -p ~/.qterm
ln -s ~/config/qterm/address.cfg ~/.qterm
ln -s ~/config/qterm/qterm.cfg ~/.qterm
if [ -f /usr/local/LumaQQ/QQWry.dat ]; then
    ln -s /usr/local/LumaQQ/QQWry.dat ~/.qterm
fi

4 一些额外设置

除了收集配置文件外,我们顺便再使用这一机制实现一些额外功能,简化在一个新的环境中初始化工作环境的工作。

4.1 创建常用工作文件夹

~/config 中创建 directories 文件夹,里面放一个 settings.sh,内容如下:

#!/bin/sh

mkdir -p ~/workspace
mkdir -p ~/Public

4.2 Mail

~/config 中创建 mail 文件夹,里面放一个 settings.sh,内容如下:

#!/bin/sh

mkdir -p ~/Mail
touch ~/Mail/mailbox
touch ~/Mail/postponed
ln -s /var/spool/mail/`whoami` ~/Mail/spool

4.3 自动运行各个 settings.sh

打开我们之前创建的 ~/config/settings.sh,在尾部添加以下语句:

...

for file in *
do
    if [ -d $file ]; then
        pushd $file > /dev/null
        if [ -f settings.sh ]; then
            echo Launching $file/settings.sh
            ./settings.sh
        fi
        popd > /dev/null
    fi
done

这样我们只要运行这个 ./settings.sh,它就会自动把所有其它目录中的 settings.sh 都运行一遍了。

5 同步脚本

为了更方便地与其它机器同步,滇狐提供了以下两个脚本:

#!/bin/sh
# Filename: upload.sh

if (test $# = 0) then
    echo Usage: $0 host
else
    rsync -az -e ssh --delete "/home/edward/config" \
        $1:"/home/edward/"
fi
#!/bin/sh
# Filename: download.sh

if (test $# = 0) then
    echo Usage: $0 host
else
    rsync -az -e ssh --delete $1:"/home/edward/config" \
      "/home/edward/"
fi