system-config原理与使用的简要说明

首先,system-config只是对Linux系统的一些增强,并且最大程度的保证对系统原有功能的兼容,所以你可以像使用普通Linux系统一样使用system-config,并不需要特别的学习。这里说一下system-config里最有用、不容易发现的一些配置用法、原理等。可能你已经看过我的十年Linux使用经验的视频( B站Youtube ),但对里面某些功能如何使用、如何实现一头雾水,这里做一简单介绍。

1 system-config原理

从系统命令行用户界面来说,system-config主要使用的shell是bash,对shell进行增强,不外乎以下几种方法:

1.1 改变系统原有行为

比如Bash自带的cd命令,使用起来有点麻烦,所以我先用 cd_bhj 作为名字定义了一个函数,然后再把 cd 别名到 cd_bhj ,这样每次我在命令行上打 cd 的时候,实际调用的是 cd_bhj 函数,这个函数会检查我给的参数,然后

  • 如果所给的参数是原本合法的 cd 调用,则通过Bash的builtin关键字,调用内置的cd命令。这一点保证了与系统的兼容性,确保system-config不会是一个全新的系统,而是Linux用户不用重新学习、拿来就能用的。
  • 如果所给的参数是原本非法的 cd 调用,计算出我实际想去的路径,再调用内置的cd命令。这样有可能我可以打很少的几个字,就cd到一个很深的目录下去,比如下图所示

    cd-t2-fr-base.png

还有一种改变系统原有行为的方法,即提供一个与系统程序同名的程序,并且把它放到PATH环境变量的前面,确保系统执行的时候会先找到你的版本。在你自己的版本里,你可以选择是否调用系统原有的版本来完成你想要达到的效果。比如我写了一个对命令行参数更友好的 ~/system-config/bin/rdesktop ,在里面它会调用系统的 /usr/bin/rdesktop

顺便说一句,想知道某个Bash命令有没有被修改过,有几种方法:

  • type CMDtype -a CMD 这里 type 是Bash的一个Builtin(内置命令),也是最实用的方法,因为不论CMD是Shell内置命令、Shell别名、Shell函数还是外部程序,它都能告诉你相应的信息。
  • which CMD 这个其实没有 type 功能全,因为 which 是一个外部程序,不是Bash内置的,所以它只能告诉你CMD是由哪个外部程序提供的,如果CMD其实还是一个 Shell别名 或/和 Shell函数的话,which是无能为力的。
  • alias 不带参数运行Bash的 alias 内置命令,它会列出你当前Bash会话中的所有alias。
  • hash

    hash 是一个Bash内置命令,它会告诉你当前的Bash记住了哪些外部程序的位置(通过用hash这种数据结构,这也是该命令名字的由来)。比如你输入ls命令,Bash会在PATH环境变量的每个目录下依次寻找ls,最后比如在 /bin/ls 这个位置找到了,那它就会记录下来 ls => /bin/bash ,这样下次再运行 ls 命令,Bash就不用重新搜索 PATH 里的每个目录了。

    下面还会提到hash的一个用途。

1.1.1 bash提供的对系统定制增强的机制

这里顺便总结一下,bash都提供了哪些机制可以用来定制你的系统。

  1. 改变环境变量,比如PATH环境变量,命令行提示符变量等。
  2. 改变bash变量,比如 set completion-ignore-case on ,可以在补齐文件名的时候不区分大小写(这样比如你输入一个 /bin/Bas ,再按一下tab键,也能正确的补齐为 /bin/bash 。当然,这只是为了方便你验证这一功能而举的例子,实际操作中刚好相反,是为了 避免 按Shift键输入大写字母而设置这个变量)。
  3. 使用bash alias(别名)
  4. 定义bash函数

    如果你定义的函数或alias与某bash内置命令或某系统自带程序同名,在这个函数里你又想调用该内置命令或程序,你可以使用bash的 builtincommand 关键字。

    比如我的ls其实是一个别名:

    alias ls='ls -hF --color=tty --show-control-chars --block-size=1'
    

    我想在命令行上交互地调用真正的ls程序,我可以用 command ls 命令,或者也可以用 /bin/ls 。又比如我的cd也是一个alias,因为它也是bash内置的关系,想调用bash内置版本cd的话,可以输入 builtin cd DIRECTORY

  5. 自定义补齐机制

    这个可以参考debian/ubuntu下自带的 bash-completion deb包。也可以参考bash info手册。

    system-config自定义的补齐机制脚本都放在 ~/system-config/.bash_completion.d/ 下。

  6. 自定义快捷键

    在bash下有很多自定义的快捷键,比如Ctrl-r/Ctrl-s可以用来向前/向后搜索历史命令,这个非常有用,有很多同学只知道用Up/Down方向键一条一条的找历史命令,或者用Left/Right方向键一个字符一个字符的移动光标。其实找历史命令和移动光标都可以用Ctrl-r/Ctrl-s来做,很多情况下是最高效的!(其中移动光标的话用Ctrl-r/Ctrl-s找到正确位置之后,需要按一下Ctrl-j)

    我自定义的最有用的bash快捷键是 Alt-k ,快速把当前命令行上的输入放置到系统剪贴板中,方便我先在命令行上试验,然后把试验成功的命令拷贝、粘贴到Emacs里正在编写的脚本程序里。

    可以用 bind -p|perl -ne 'print unless m/not bound|self-insert/'|less 命令来看一下你的系统上bash定义了哪些快捷键,然后选几个你认为很有用的慢慢学起来。

    想知道如何自定义bash快捷键的话,可以参考 ~/system-config/.inputrc ,还有 readline 的 man 手册: man readline

1.2 加入系统原来没有的程序

这样的程序我可能写过很多,接下来我会挑几个重要的说一下。比如beagrep/grep-beatags,是阅读代码的利器,其中集成了一个搜索引擎,所以速度非常快。见 中文版博客 / 英文版博客 。事实上,我非常推荐你用system-config本身提供的这些阅读源码的程序,来了解system-config本身,比如我想搜索上面提到的 cd_bhj 是在哪里定义的,我在~/system-config目录下一运行这个命令,马上就出来了: grep-beatags -e cd_bhj 。关于怎么用,非常简单,下文马上就会提到。

1.2.1 删除系统中原有的(自己写的)程序

不能否认,有时候会写完一个程序,最后发现用途不大,或者其实系统里通过 apt-get 可以下载、安装一个功能类似的软件,为了避免自己重复造轮子,这时候会考虑把自己造的半调子轮子给删掉,这样就恢复了系统原有的行为。

但这时候可能会出现一个问题。比如我写了一个rdesktop的封装命令,放在 ~/system-config/bin 目录下,但后来我又决定还是用 /usr/bin/rdesktop 就可以了,于是我把 ~/system-config/bin/rdesktop 删了,这时候再运行 rdesktop ,你会发现这样的输出:

-bash: /home/smartcm/system-config/bin/rdesktop: No such file or directory

这是怎么回事儿呢?它不应该帮我执行 /usr/bin/rdesktop 吗,怎么还去 ~/system-config/bin/rdesktop 这个位置找然后抱怨出错找不到呢?原因就是上面提到过的 hash 机制,删掉 rdesktop 后,当前的Bash会话的hash里还记着它原来的位置呢!这时候你可以用 hash -d rdesktop 这个命令清掉 rdesktop => ~/system-config/bin/rdesktop 的 hash。或者你关闭、重新打开一个Bash终端会话,也是可以的,因为Bash的hash是放在内存里、临时性的,每个进程都随着自己程序的执行慢慢的创建hash缓存。

1.3 组合调用以上

比如上面的 cd 的增强,如果计算到有多个路径与我给出的参数匹配,则 cd_bhj 会调用我写的 select-output-line 命令,对我显示一个命令行上的简易选择框(事实上根本就没有框)。见下图:

cd-fa-base.png

在以后你会在非常多的地方见到这个对话框。

1.4 system-config是一个采花大盗

为什么说这个对话框会很常见呢?因为我就像一只勤劳的小蜜蜂一样,看见一朵花很漂亮,就飞过去猛采一通。同样的,如果我发现某个软件的某个功能很好用,我就会想办法给它移植到system-config里来。比如你看到的那个命令行选择框,其创意就来自于Emacs下的helm.el(和以前的anything.el),见下图:

emacs-help-pack.png

类似的,你还会看到 system-config 里有时候会问你这样的问题(注意大小写,Yes/no代表直接回车相当于输入了yes;yes/No代表直接回车相当于输入了no,一般我会选一个我认为比较合适的选项作为默认值):

cli-yes-or-no-p.png

这个也是从Emacs里学来的(我的命令行命令也叫yes-or-on-p,跟Emacs下的函数一模一样):

emacs-y-or-n-p.png

2 system-config使用

System-config里提供的最有价值的功能,应该就是它的全套的阅读源代码的解决方案。我要研究任何项目的代码,都离不开这些工具。包括system-config自身的代码,我也会用这些工具来进行快速搜索、阅读。所以在使用部分,我们一开始就先介绍一下这些工具。

2.1 阅读源代码

要成为一个高级的程序员,快速的阅读、理解代码的能力是不可获缺的。阅读代码除了打开一个源码文件一行一行的从头读到尾这个笨办法之外,经常需要进行以下三种操作:

  1. 搜索某一字符串在代码中出现的位置
  2. 搜索某一函数、变量被定义的位置
  3. 搜索某一函数、变量被使用(调用)的位置

System-config提供了快速搜索、定位代码的业界最简单方法,只需要以下几个步骤:

  1. 创建索引。在源代码的顶层目录运行
    for-code-reading
    

    以下所有步骤,都是以已经用这个命令创建完索引为前提的。

  2. 搜索字符串。比如在源代码的任意子目录下运行
    beagrep -e "hello world"
    

    以搜索 hello world 这个字符串在源码中出现的位置。

  3. 搜索定义。在源代码的任意子目录下运行
    grep-beatags -e "readlink"
    

    以搜索 readlink 被定义的地方。

  4. 搜索引用。在源代码的任意子目录下运行
    grep-func-call -e "readlink" -a --nc
    

    以搜索 readlink 被调用的地方

这些程序都可以在命令行上运行,但我平时一般都是通过在Emacs下调用它们,效果更好,比如可以直接跳转到搜到的文件:行号上。

各个搜索程序都有一些更复杂的用法,这个目前只能通过阅读相应的脚本文件以获得(此外可以参考一下上面提到的关于beagrep的中英文博客)。再次强烈建议使用system-config提供的这些程序本身来阅读system-config自身代码,以及其他所有工作中要用到的代码。我拿到任何代码,第一步是就是用 for-code-reading 创建索引。

顺便说一句,以上这些都是比较细节的阅读代码工具。事实上有个大牛被问到“你如何阅读一个新项目的代码”时的回答,让我十分震撼:“我一般先用find/ls看一下里面的文件、目录结构,获取一个整体印象”。见 Coders at Work 一书。

2.2 对Terminal界面(命令行提示符)的改进

接下来我大致按照system-config的各种改动的常用、常见程度,介绍一下我认为比较有用的一些改动。首先是对命令行显示界面的改动,这个是最显著的,所以放在前面说一下。

默认Linux的命令行提示符是这样的:

default-ps1.png

增强后的提示符是这样的:

system-config-ps1.png

在这里你可以看到,原来只有一行的提示符,现在变成两行了,颜色也更丰富了。有些人非常受不了提示符被改变,一个劲的追问该怎么改回去,甚至因此就放弃了 system-config 或者忍不住差点破口大骂。其实大可不必。我就不告诉你怎么改,你自己 必应 一下吧。这里我只说一下我这么改的理由、好处。

  1. 我可以不折行输入的命令长度最大化了。

    默认因为都是在一行上,并且提示符里包含了当前路径,所以输入的命令稍微长一点,就会发生折行。并且随着当前路径的深度而变化。

  2. 以前无法用鼠标双击选中当前路径名,必须用鼠标按下拖曳;现在因为在其前后各有一个空格,所以你鼠标双击一下,就能选中整个当前路径。

    注意这是我以前觉得非常有需要的一个功能,现在我用更好的方法实现了,那就是 up 系列命令,接下来会讲到。

  3. 可以更方便的显示更多信息。

    比如上面你看到的是远程登录的提示符, 其中有 Remote:True 字样。并且上一条命令失败的话,会显示返回值是多少,失败的时间是几点几分。

    如果是本地登录的话,颜色会更花哨,并且我可以方便的自定义更多的显示信息(通过 start_recursive_shell 函数或者其它会调用该函数的命令):

    start-recursive-shell-ps1.png

    比如上图中,我启动了一个代理(你懂的),然后用 adb -s 命令设置了一下当前的adb设备(后文会提到我对adb的这种封装)。

2.3 对系统剪贴板的增强

从cygwin下第一次发现它提供了在命令行上操作系统剪贴板的程序putclip/getclip,于是一发不可收拾,我在Linux底下也google了一下有没有类似的程序,还真有,叫xclip。但因为我已经习惯了cygwin下的putclip/getclip,所以我在Linux下也封装了一下xclip,写了Linux版本putclip和getclip脚本。以及其他一系列操作路径的脚本。以下是用法:

  1. putclip 不加参数,会从stdin读取文本,放到剪贴板中
  2. putclip ARGS... 会把所有 ARGS... 拼成一个字符串,放到剪贴板中
  3. up/wp/swp/sup/bp等一系列程序,分别用某种格式拷贝当前路径或所带参数的路径。举个例子:在安卓代码 ~/src/android/frameworks/base 目录下,运行 ap CleanSpec.mk ,会输出 frameworks/base/CleanSpec.mk ,因为这是这个文件的“Android Path”,这也是ap这个名字的由来。类似的还有gitp等等。

    这些Xp程序用于命令行界面与图形界面之间的通信是最方便的。比如有时候在Firefx/Email客户端下要上传一个文件,在图形界面上一层一层的改变目录、找到文件有时候挺麻烦的,有了up命令和system-config下方便的cd增强机制,很容易找到文件并拷贝其路径,这样在Firefx/Email客户端里一粘贴就好了。还有一个场景是系统设置里想配一下默认用哪个浏览器程序,我想配置成 /usr/bin/chromium ,如果用图形界面切到 /usr/bin 目录下的话,你会发现需要等待好长一段时间—— /usr/bin 目录下文件太多了,图形界面需要把它们全部显示出来相当费功夫,这时候用 up $(which chromium) 直接一拷贝一粘贴就搞定了。

还有一个对剪贴板的增强,是在命令行上输入或用历史机制调出一条长长的命令之后,用一个快捷键把它拷贝下来(然后贴到Emacs里正在编辑的脚本里、邮件正文里等等)。这个在之前已经提到过了,快捷键是 Alt-k,也可以按 Escape k

2.4 对Bash历史纪录的增强

有些同学可能还不是很了解,Bash下用Ctrl-r/Ctrl-s可以交互式的用搜索的方法调出之前一段时间内运行过的命令。我经常用这两个快捷键。但是很多时候还嫌这两个键不够方便,所以我通过Bash的补齐机制,定义了一个re命令。使用方法如下:

  1. 运行一下hir命令(不是每次都需要,如果你发现你最近使用过的命令补齐不出来,可以这时候才用一下hir)。
  2. 输入re,然后输入你想调出的历史命令的几个子字符串。
  3. 按Tab键补齐。如果只有一条匹配的历史命令的话,会直接上屏,回车即可运行;如果有多条的话,可以再输入 .0 / .1 / .N 之后再按Tab,会选中第N条(从0开始数)匹配的历史命令。

举例:我之前运行过这样的命令、

my-rfa 'p=$(ap); P=$(repo-project); cd $ANDROID_TOP/.repo/projects; git clone --bare $PWD/$p.git /d/Downloads/android/$P.git; '

下回我想重新运行这条命令的话,用 Ctrl-r/Ctrl-s 可能不是很方便,有时候甚至找不回来(因为bash自带的历史文件 ~/.bash_history 最多会记N条,老的会被冲掉)。所以我输入 re my rfa ap ,然后按Tab,会给我补出system-config的历史文件 ~/.cache/system-config/.bash_history.bak 中匹配以上三个字符串 my rfa ap 的所有命令,我再输入相应的 .N 就可以选中实际我想要的第N条匹配的命令;也可以输入更多字符串使匹配更精确,再按Tab键更新缩小补齐选项,方便选择。

这个主意受了Emacs下文本补齐方法 hippie-expand 的启发,其中有一条补齐方式就是在当前正在编辑的文件中寻找与当前输入的“部分文本”相匹配的更长的文本。

另外,上面提到的hir命令会做两件事儿,

  1. 把 ~/.bash_history 里的历史命令去重(去掉重复)保存到 ~/.cache/system-config/.bash_history.bak 里,方便给re补齐用
  2. 把 ~/.bash_history 里的历史命令全部读到当前的bash会话中

    为什么要做这件事儿呢?因为很多时候会打开多个终端窗口(或通过gnome-terminal/konsole/xfce4-terminal的新Tab,或通过screen/tmux命令),在一个终端窗口A下输入过一个命令后,在另一个已经存在的终端窗口B下是无法调出此命令历史的。想要能调出来,需要做两件事:1. 在终端A下保存历史命令到 ~/.bash_history ,这个可以手动做(Bash内置命令history,加-w参数) ,也可以设置bash每输入一个命令就自动保存,而非缓存在内存中,system-config采取的是自动存;2. 在终端B下把 ~/.bash_history 的内容重新读到当前Bash会话的历史中(同样用history内置命令,加-r参数)。

    以上。

2.5 一些非常常用的命令介绍

2.5.1 s

s代表search的意思,输入 s hello world ,会提示你选择哪个搜索引擎,然后用你选定的搜索引擎去搜索“hello world”。

2.5.2 e

e代表edit的意思,在终端上输入 e FILENAME ,会弹出当前正在运行的 emacs 窗口,并在其中打开 FILENAME 这个文件。它是对emacsclient的一层简单封装。

2.5.3 ew

ew代表edit and wait的意思,像上面的e一样,也调用了emacsclient,只不过它还会等待Emacs文件编辑结束。所以我把它配成了我的 EDITOR 环境变量。

需要注意的是,这两个命令都支持在远程的ssh登录下使用,会在本地的Emacs窗口下打开远程文件进行编辑。

2.5.4 of

of代表open file的意思,在终端上输入 of FILENAMEof URL ,会用系统默认的关联程序打开相应的文件或网址。这个相当于Windows下用鼠标双击了某文件的图标。在Windows命令行cmd.exe下也有个相应的start命令;在cygwin下也有个cygstart命令;在Mac下好像也有个叫open的命令。

2.6 Emacs介绍

Emacs是一个非常强大的编辑器。很多人觉得太难了,不想学它或学了一阵子之后又放弃了。其实很多工具用下来最后比的还是看谁更能坚持。坚持用Emacs,坚持不懈的折腾它,何尝不是工匠精神的一种体现呢。工匠们一般都很在意自己使用的工具称不称手的。而编辑器对程序员来说,是最重要的工具了吧。

其实Emacs下有很多快捷键,跟Bash下是一样的。你可能都已经学会了,比如 Ctrl-a 是移到行首, Ctrl-e 是移到行尾。 Ctrl-r/Ctrl-s 是搜索,等等等等,所以还有什么可怕的呢(另外你可能发现了,UNIX系统下各种idea相互杂交是非常普遍的,比如把Emacs的按键在Bash里也实现一番。所以system-config从Emacs下借鉴helm.el到命令行下等并不是首创,只是延续了这一传统)。

在学习Emacs之前,强烈建议仔细阅读一下Vim作者写的我翻译的 高效文本编辑的七个习惯 。对于掌握任何一门技术、一种工具,几乎都是非常有用的。

比较基本的Emacs使用就不讲了,你可以从它的Help菜单里的Emacs Tutorial开始,第二个菜单项(choose language)里有中文版的。下面讲一下我认为比较重要的一些用法。从我认为最重要的获取帮助、阅读文档开始,因为我认为掌握这些技能,对你进一步深入学习Emacs和其他很多Linux程序都是很有帮助的。

2.6.1 用Emacs获取帮助、阅读文档

Emacs是那种螺旋上升的工具,在你开始使用它之后,会不停地增强自己的能力。就像学语言一样,边学边用,用得越多,学得越好;学得越好,用得越爽。这是一种很少见的自增强的工具,造成这种现象,有几种原因,你可以好好利用一下这些特点:

  1. Emacs自带的帮助系统,是强大到没有谁了的。可能主要是因为Emacs是用Lisp写的,而Lisp系统可能都有非常强大的自我帮助功能。几乎任何一个Emacs命令、快捷键,都可以方便的查看它的帮助。一定要好好利用这一点。
  2. Emacs提供了非常方便的查看系统man手册,info手册,perldoc等各种手册的功能。好好利用Emacs自带的搜索功能、info自带的搜索功能,往往可以达到事半功倍的效果。
  3. Emacs下提供了Occur命令、helm.el命令,对它们善加利用,你会发现Emacs提供了自发现的功能,允许你主动的去 发现 系统有哪些功能。比如你当前正在试着用Emacs的info去读bash的info手册,但你对info模式还不是很了解,怎么办呢?

    你可以在info模式下按一下 Ctrl-h b ,Emacs会列出所有当前模式下的快捷键。然后你用Emacs的occur命令,过滤出所有与info相关的快捷键。这下你就可以很轻松的发现有哪些与看info手册相关的有意思、值得一学的快捷键了:

    info-occur.png

  4. 用Emacs打开某一程序,研读其源代码,善用Emacs的搜索功能、system-config的搜索工具,有时候比读文档更有效!

2.6.2 在Emacs下调用system-config提供的阅读代码程序

之前提到过,我一般都是用Emacs阅读代码。所有的代码搜索相关的操作,基本都是用Emacs及其grep模式实现。其中比较重要的几个快捷键是:

M-g r
默认运行 beagrep 程序,查找任意字符串。快捷键助记法: M-g r 后两个字母是 grep 的前两个字母。
M-.
默认运行 grep-beatags 程序,查找函数、变量的定义
M-g f
默认运行 grep-func-all 程序,查找引用
M-g o
运行我定制过的 bhj-occur 命令,是对Emacs自带的occur的一点封装
M-g i
Emacs自带的imenu命令,可以用来方便的列出一个代码文件里有哪些函数、全局变量;通过helm.el你可以方便的过滤你感兴趣的函数、变量;并在这些函数、全局变量之间跳转

注意我专门把这个imenu命令的用途用三个分号隔开了,因为和上面提到的Occur命令一样,在阅读源代码的时候可以用来大幅缩小阅读的范围。就像上面提到的有个大牛说自己读代码先用find/ls看一下目录结构一样,用imenu/occur先看一下一个长长的代码文件里有哪些函数、全局变量,是非常高效的。这与Emacs自己对imenu命令的帮助文档是不一样的,Emacs只列出了imenu的一个用途,就是用来跳转,就像find/ls的man手册里也不会告诉你,可以用它们来阅读源码时看一下代码结构:

imenu is an interactive autoloaded compiled Lisp function in
‘imenu.el’.

It is bound to M-g i.

(imenu INDEX-ITEM)

Jump to a place in the buffer chosen using a buffer menu or mouse menu.
INDEX-ITEM specifies the position.  See ‘imenu-choose-buffer-index’
for more information.

2.6.3 从Emacs调用外部程序

Emacs下有一系列的函数,可以调外部程序来完成你的工作。

  1. 调用make等进行编译
  2. 调用grep等进行搜索(前面提到的代码阅读程序都是用的这一机制)
  3. 调用某shell命令,显示其输出的文本
  4. 调用某shell命令,并把当前选中的文本pipe给这一程序进行处理,显示其输出的文本
  5. 在上面的基础上,把当前选中的文本替换为处理完成后输出的文本

2.6.4 从外部程序调用Emacs

这个可以通过emacsclient实现。所以Emacs和终端基本是通的。双方可以相互调用,像搭积木似的,通过一些简单的基本模块,组建出非常强大的功能,以便更高效的完成你的工作。

2.6.5 Emacs下的补齐

Emacs下有各种补齐功能,这里讲几种我最常用的

  1. yasnippet,比如输入codegen,这是一个我准备过的 yasnippet ,然后按一下 C-M-i ,就能补齐
  2. codegen,被yasnippet展开之后,按一下 M-s g ,就能生成代码,这些在我的那个视频里有介绍
  3. bbyac,输入一小段文字,按一下快捷键,被匹配、展开成当前文本中已经存在的文字。
  4. Emacs自带的hippie-expand,这个用得不多了,基本上前面几个足够覆盖了它的功能。

以上这几种工具,是可以被应用于任意编程语言的,并且比较简单易用,所以单独说一下。除此之外,还可以自行研究一下如何配置你在用的编程语言的补齐及其他增强工具。比如C/C++程序,通过调用clang编译器,可以进行上下文智能补齐,非常强大,但用起来也比较折腾一些… 还有我写的ajoke.el,用于Java编程,这个就更扯了…

2.6.6 org-mode

做为一个走技术路线的工程师,如何管理好自己的时间、项目进度是非常重要的。org-mode是一个非常强大的项目管理工具。强烈推荐。你都不会相信这么简单的一个工具会有这么强大的效率提升。其实人在很多时候并不需要多么花哨的工具,养成习惯,用一支笔,一张纸,列一个待办事项清单,然后一件一件的去做掉,去划掉,就是最强有力的执行力。当然,除此之外,就需要动脑筋思考自己的清单里哪些是重要的,哪些是可以放弃的,哪些是可以拖一下的,哪些是可以交给别人去做的…

另外,org-mode是一种强大的写文档的工具。我的博客全都是用org-mode写的。

最后,我还偶尔会用org-mode搞一下文学编程,一边把自己的想法写出来,然后一边写代码去实现它。编程是一件很难的事,有时候可能难就难在头绪太多,把所有想法都放在脑子里,脑子就会有点儿不够用了,但如果你把很多想法都写出来,清空自己的脑子,最后就变成了做选择题,从自己写出来的想法中选一个或几个最靠谱的开始搞起来。所谓万事开头难嘛,没想到org-mode的文学编程可以让开头不再难。但至于是人都有的拖延症,就是另外的问题了。

2.7 Sawfish介绍

Sawfish是一个窗口管理器,我在system-config中把它和xfce4配在了一起,后者提供了一个稍为更加现代化一点,但还是非常轻便的桌面系统。这样我就有了一个比较舒服的桌面环境。

Sawfish的主要功能有:

  1. 对窗口进行各种操作:最大化、最小化、调到前台等等。甚至可以自己创建、画窗口。
  2. 对窗口可编程发送键盘事件。相当于拥有了Windows下的按键精灵的功能。
  3. 用system函数在后台启动系统程序(相当于sawfish->命令行)
  4. 提供sawfish-client程序,可以方便的从命令行对sawfish进行控制(相当于命令行->sawfish)

    所以命令行和sawfish基本也是通的,就像命令行与Emacs一样。(事实上用于Sawfish配置的语言,也是一种Lisp方言,就是从Emacs Lisp演变来的)。

常用快捷键(这里s-代表Super-,也就是一般键盘上的Win键):

s-h s-t
调出或启动终端
s-h s-m
调出或启动Emacs
s-h s-n
调出或启动Firefox
s-h s-s
调出窗口列表,可键入文本进行选择,C-n/C-p上下选择窗口,回车将选中窗口调到前台
s-h s-r
相当于Windows下的Run Dialog,输入命令以运行
s-h k
快捷键的帮助,提示你按一个键,给出Sawfish对该键的相应绑定
s-h s-k
更复杂的快捷键的帮助,主要加入了对 s-h 组合快捷键的帮助

想了解sawfish更多功能,可以运行一下sawfish-ui,里面有一些简单的绑定可以配(system-config里已经配了一些)。另外也可以打开 ~/system-config/.sawfishrc 进行查看。

可以在 ~/system-config/.sawfish/start 下添加脚本,在桌面启动时该目录下的脚本会被启动。

可以用

bhj-notify TITLE "NOTIFICATION TEXT"

弹出一个简单的sawfish桌面通知。

2.8 其他效率工具介绍

2.8.1 screen

screen是一个终端管理工具,可以打开多个终端,方便的在终端之间切换。可以花时间学习一下它的快捷键。推荐程度:5星。

2.8.2 expect

Tcl编程语言的一个插件,用来实现一些命令行上的自动化非常方便。我的my-adb(对adb的增强,下文会提到)有一部分就使用了expect,另外还有一个bbs聊天工具,bbs-robot.exp,也是用expect写的。推荐程度:4.5星。

2.8.3 小扳手

我写的一个从PC自动操作手机的程序,相当于给手机增加了一个按键精灵。操作手机的脚本都是用lua实现的,这部分是开源的。

可以用它方便的聊天、拨打电话、发送邮件、发布社交软件信息(比如微博和/或微信朋友圈和/或QQ空间)、查找联系人、不仅仅是手机系统联系人、还包括QQ/微信联系人等等等等。

2.8.4 Microsoft Natural Ergonomic 4000 键盘

几个优点:

  1. 手腕自然弯曲,不累
  2. 键大,不容易按错
  3. 在sawfish下可以自定义的快捷键多,比如最上面的一排,我在sawfish下分别定义成了如下功能:
    Edit with emacs
    把当前GUI程序下在编辑的文本全部选中,发送给Emacs,在Emacs下进行编辑,编辑完成后把结果文本粘贴回到原来的GUI程序下。这样有时候我需要在Firefox、小扳手里进行大段的格式化文本编辑的时候,我可以用Emacs来做。
    Search
    打开我目前在独自维护的 beagle 桌面搜索引擎
    Emacs edit complete
    结束Emacs下对某个文件的编辑(该文件是被edit-wait/ew程序打开的,有“人”在等待它编辑结束,比如git commit message的编写,按一下这个键保存commit message,git commit就可以继续进行了)。
    Org-agenda
    上面提到过org-mode,这个快捷键帮你把你所有的待办事项方便快捷的整理成一个一目了然的清单。
    小扳手快捷键
    打开小扳手或将其调到前台

2.9 附:将system-config最大化发挥作用的方法

  1. 选用Sawfish+Xfce4作桌面(系统登录时选sawfish即可)
  2. 选用Emacs作编辑器
  3. 选用Firefox作浏览器,装system-config自带的Firemacs插件(用link-firemacs命令)
  4. 使用 Microsoft Natural Ergonomic 4000 键盘

建议你不要一上来就这样做,一步一步来,步子太大了容易扯着蛋,造成休克式学习。强烈建议这时候再打开Vim作者写的我翻译的 高效文本编辑的七个习惯 一文读一读。把注意力放到自己需要完成的工作上,由此出发思考用哪些工具可以更好更快的完成自己的工作,而不是白学一堆看上去很高效,实际中却用不到的功能。

最好能学会如何打造最适合自己的工具。如果你的工具箱中的一部分,是来自于system-config,那我就很荣幸了。

如果你不信邪,或者有点轻微的自虐倾向,那你尽管放心大胆的用你喜欢的方式使用system-config吧,所有代码都是开源的,随便你怎么折腾。

2.10 安卓开发相关

如果你不做安卓系统开发,可以直接跳过这部分内容,剩下的都不用看了。

2.10.1 源代码相关

我对git/gerrit/repo有相当多的封装。主要是通过gerrit/repo工作的时候,一般需要处理多个产品、多条线的问题。在提review的时候,一定要搞清楚当前应该用哪个git remote,应该提到哪个git branch上。手动输入这些信息是非常痛苦的,一会是这个分支,一会又是那个分支,很容易搞错。即使不搞错,也容易打错字。

所以我提供了一系列 repo-XXX 命令,比如repo-branch,可以给出当前git仓储提review的时候应该提到哪个分支。其规则如下:

  1. 一般情况下,从repo的manifest里可以获取分支信息
  2. 如果你用 git checkout -B ...git branch --set-upstream ... 设置过当前分支及其对应的远程分支,则repo-branch会打印远程分支的名字,这种情况下提review一般就是要提到那个分支下。

另外我需要提review的时候,不会直接输入 git push $(repo-remote) HEAD:refs/for/$(repo-remote) ,虽然这条命令是OK的,而是会用更进一步的封装 gerrit-push-review ,有几个好处:

  1. 会自动先把你的代码rebase到服务器上最新提交
  2. 可以在本地先自我review一下代码(加-R参数可以不review)
  3. 可以在后面加gerrit用户名,直接添加reviewer

2.10.2 my-rfa

repo自带的repo forall命令运行起来非常慢,所以我提供了my-rfa命令,运行速度能快一点。

time repo forall -c 'pwd'
time my-rfa -j1 'pwd'

同样是列一下所有仓储的路径,第一条命令第一次运行花了1分多钟,之后每次都要5、6秒钟,后者每次都只有不到1秒钟。

2.10.3 repo-changes?

在安卓代码顶层目录下运行,会帮你列出哪些仓储有改动。

2.10.4 repo-cherry-find-all/repo-cherry-pick-all/repo-cherry-push-all

这一系统脚本,用于方便的进行安卓开发基线升级。

2.10.5 编译相关

安卓编译系统有较多问题,所以我对其进行了一些封装。官方文档的流程是这样的:

  1. . build/envsetup.sh 设置环境
  2. lunch 选择编译目标产品和编译类型
  3. make/mm/mmm等

这样做有以下几个问题:

  1. 每次新开Bash窗口都要做,比较费劲
  2. 容易出错,首先是容易忘了做过了还是没做过,还没配env/lunch就开始make,结果编译的是错误的产品;或者多次冗余的进行env/lunch,浪费了时间。
  3. 其次是在多产品的情况下更容易配错,比如刚刚配了A目录下的A1产品,过了会到了B目录下不重新配就直接进行B1产品的编译,肯定会错的一塌糊涂。
  4. 安卓4.4和5.0之间升级了对Java版本的要求,前者用oracle的java6,后者用openjdk的java7,如果你同时开发4.4和5.0版本的话,需要自己按照当前产品的安卓版本进行设置

其实Android自己提供了一个机制,方便你一劳永逸的配好一个代码目录,而不是每次都重新配一遍。那就是代码顶层目录下的 buildspec.mk 文件,可以把相关配置信息放在里面,以后不用配直接make就可以。我相信谷歌提供了这个机制,她内部的员工肯定都会用该机制,而不是只像官方文档里描述的那样,每次都那么麻烦。

所以我提供的 android-make 就利用了这一机制,你可以通过 -c 参数比如 android-make -c aosp_x86-eng 指定用哪个编译配置,之后也可以不加-c参数,那么它就会用上一次加 -c 参数时指定的配置。

2.10.6 加快编译速度

在做安卓开发时,非常重要的一点是要尽量加快编译速度。除了让公司给你配更好的机器外,以下有几个方法:

  1. 用ccache,对full build来说,用过ccache之后速度能快一倍。
  2. 避免读取所有Android.mk文件。比如想编译kernel或者lk,很多开发都只知道如何通过make去做,但这样会把所有Android.mk都读一遍,这是非常费时间的。所以可以对安卓编译机制做一番研究,最后得到怎样用类似mm的机制编译kernel、lk、sepolicy等的方法。如果是做这方面工作的朋友,可以关注一下。
  3. 用mmm只编译相关模块,避免full build。

2.10.7 对boot.img进行拆分、合成

在系统相关的开发中,尤其是bsp工程师,以及需要改系统启动脚本(init.rc系列文件)、sepolicy的情况下,对boot.img进行简单改动,是非常有用的一项技能,有时甚至完全不需要通过build来进行,比如对init.rc的改动。这个在system-config底下是通过 replace-bootimage 及其辅助脚本实现的。

2.10.8 重刷某个分区

这个有很多种方法,比如fastboot,比如手机芯片厂商提供的刷机程序。但有一种更方便的方法,是在手机运行的过程中,把分区的刷机文件用adb push上去,然后用dd命令写入到相关分区。我为此专门封装了一个 adb-push-partition 的脚本。

2.10.9 伪工厂重置(清空/data目录,但保留某些重要文件)

有一个名为 adb-clean-data 的脚本,用来做这个事。

2.10.10 对adb的增强

说到这儿,讲一下我对adb的一些增强。

system-config里的adb像cd一样,是一个别名,实际调用的是my-adb脚本。以下是与adb相关的最常用的一些增强:

  1. adb -s 直接设置 ANDROID_SERIAL 环境变量,如果有多个adb设备,非常方便,并且会在命令行提示符上显示当前bash会话里选的是哪个adb设备。
  2. adb COMMAND 直接运行相关的 COMMAND ,相当于输入了 adb shell COMMAND ,但不需要多打一个 shell (前提是 COMMAND 不是adb自带的子命令,比如 syncadb sync 是用于同步本地文件夹到adb设备的system或data分区; adb shell sync 则是调用 adb 设备上的 sync 命令,这是一个Linux系统程序,用于命令Kernel把内存中的缓存数据写回外部存储设备)。
  3. adb COMMAND ARGS 直接相当于在交互的 adb shell 下输入了 COMMAND ARGS ,不需要额外加引号,因为有时候稍微复杂一点的命令,把引号加正确就变成一件很困难的苦差。

    比如

    command adb shell echo 'hello    world'
    

    你会看到结果跟先启动adb shell,然后再输入

    echo 'hello    world'
    

    是不一样的,因为adb把引号给“吃掉”了。但system-config里,你可以直接输入

    adb echo 'hello    world'
    

    最后得到更合理的那个结果。

    同理, adb A_QUOTED_STRING_WITH_SPACES_IN_IT 会把后面的参数交给 sh -c ,也就是说,在system-config下输入

    adb 'echo "hello    world"'
    

    结果与先启动 adb shell ,然后输入

    sh -c 'echo "hello    world"'
    

    是一样的。这个特性我想应该是从Perl的system函数得到的启发,参考 perldoc -f system

  4. 其他一系列与adb相关的脚本,比如adb-push和adb-pull,adb自带的push和pull只支持一次处理一个文件或文件夹,于是我简单的封装了一下,adb-push/adb-pull一次可以处理多个文件或文件夹。

    刚刚简单的看了一下,我的system-config的bin目录下,大约有170个adb相关的命令…