命令与征服

by Chunhao

首先,这篇日志和技术有关,应该用英文写的,但里面夹杂了一些中文的元素,就写成中文了。

这个题目,作为游戏的名字就是“命令与征服”,作为Full Circle中文杂志的一个栏目名,就是“决胜命令行”。

人们总在做关于图形界面与命令行的争论,其实,二者各有优缺点。图形界面总能给人一种直观的感受,在一定程度上提高工作效率。但是,任何事都有它的局限性。如果画一个圆,里面的空间代表图形界面所擅长的工作,那么,这个圆环外面的整个空间都应该属于命令行!

命令行也有它的局限性,用手敲命令的时间有时并不一定比把鼠标移到某个菜单并点一下快。而且,尤其是对于非英语国家的人来说,了解那么多的命令和参数并不是一件容易的事情,尽管man命令可以帮助我们很多。

命令行的一个杀手应用就是批处理。如果你需要对几十个,甚至几百个文件进行类似的操作,摆在你面前的有三条路:1. 等专门的软件被写出来。2. 自己纯手工对这几百个文件处理。3.动手写个脚本。

在Linux世界,如果你要完成一项工作,有三条法则:

  1. 如果有现成的软件,使用它。
  2. 如果需要使用几个软件来完成,用管道或其他东西把他们连接起来。
  3. 否则,自己写程序完成它。

使用命令行,使用脚本,应该是介于第二条和第三条之间。脚本是用于把几个程序连接起来的工具,当然,也算是自己写的一个程序。

在批处理中,我们可以通过脚本,通过定义的一些变量,甚至一些循环、判断语句来很轻松地完成工作。当然,这一些的基础就是命令行,脚本很难通过图形界面和其他程序交互。当然,也有一些在命令行中生成GUI的工具,例如zenity

大概半年前,我做史记的电子书。其中,一个文件夹下的文件是这样的:

001.txt

002.txt

003.txt

...

130.txt

文件的开头是这样的:

史记
史记卷一 五帝本纪 第一

*集解凡是徐氏义,称徐姓名以别之。余者悉是骃注解,并集觽家义。索隐纪者,记
也。本其事而记之,故曰本纪。又纪,理也,丝缕有纪。而帝王书称纪者,言为后代纲

我要作的就是把每个文件的文件名都改成这样的:

1_卷一五帝本纪第一.txt

社区牛人们的帮助下,最终写出了如下脚本:

#!/bin/sh

for i in *.txt; do
  f=${i%.*}
  t=`head -n 2 $i | tail -n 1 | awk '{print substr($1,3,length($1)-1)}'`
  cp $i ./tt/$f$t.txt
  echo $i
done

当然,这是是个初步的脚本,以后还设计到全角半角空格的问题,文件编码等问题,就都不难解决了。

本人对相声很感兴趣,发现学院一个教授楼学庆的网页(校外可能无法访问)有几百段相声。就索性都抓下来了。但他的文件名都是1.wma 2.wma 3.wma之类的,很难知道那个文件是那段。于是索性把他的网页也抓下来了。每次先打开那个网页,然后选相应的相声听。

但这样太烦了,我总是想用Rhythmbox来管理我的音乐,这样就可以很好的利用艺术家和专辑来分类了。要做到这一点就意味着:1. 我必须把所有的wma转化成mp3,这样方便放到Mp3播放器上。2.必须把所有的“1.wma”都改成”八大改行(郭德纲\张文顺).mp3″这样的名字。3.必须对每个文件都加上tag。顺便说一点,一共有331个文件。

整个方法我构思了很久,格式转换我是借助了一个叫convertAudioFile的脚本。用到了mplayer和lame。加标签是用到了id3tag。

改文件名比较复杂,段子的名字和艺术家都在html文件里放着,我必须要根据原来的文件名从html中找到段子的名称和艺术家,然后再改名字。从html中分析我用到了Python,因为python有很好的分析html的库。我不会python,也只能现学现用。当时找了一个用python分析html的例子(现在怎么也找不到链接了,抱歉),就在上面改了改。居然能用了:

#!/usr/bin/python
# coding=utf-8

import sys
import urllib
import HTMLParser
import re

if len(sys.argv) < 3:
    print 'options: [-n/-a] filename\n';
    quit();

for i in sys.argv[1:]:
    if i == '-n':
        outputContent = 2;
    elif i == '-a':
        outputContent = 3;
    elif i == sys.argv[2]:
        inputFileName = i;
    else:
        print 'invalid option';
        quit();

class CustomParser(HTMLParser.HTMLParser):
    selected = ('table', 'h1', 'font', 'ul', 'li', 'tr', 'td', 'a')

    def reset(self):
        HTMLParser.HTMLParser.reset(self)
        self._level_stack = []
    def handle_starttag(self, tag, attrs):
        if tag in CustomParser.selected:
            self._level_stack.append(tag)
        if "/".join(self._level_stack) == 'table/tr/td/a':
            global fileName;
            fileName = attrs[0][1];
    def handle_endtag(self, tag):
        if self._level_stack \
        and tag in CustomParser.selected \
        and tag == self._level_stack[-1]:
            self._level_stack.pop()
    def handle_data(self, data):
        if "/".join(self._level_stack) == 'table/tr/td/a':
            if outputContent == 2 and inputFileName == fileName:
                print  data.lstrip('1234567890').replace('/', '\\').encode('utf-8');
            p = re.compile(u'\(.*\)');
            m = p.search(data);
            if m and outputContent == 3 and inputFileName == fileName:
                print m.group().strip('\(\)').replace(u'、', '\\').replace('/', '\\').encode('utf-8');

content = unicode(urllib.urlopen('project/index.html', None).read(), 'utf8')

parser = CustomParser()
parser.feed(content)
parser.close()

网页的一部分是这样的:

<tr><td><a href="xiangsheng/1.wma">1八大改行(郭德纲/张文顺)</a></td>
<td><a href="xiangsheng/2.wma">2论50年相声之现状(郭德纲/张文顺)</a></td>
<td><a href="xiangsheng/3.wma">3大保镖(郭德纲/于谦)</a></td>
<td><a href="xiangsheng/4.wma">4寿比南山(郭德纲/于谦)</a></td>
<td><a href="xiangsheng/5.wma">5八猫图(郭德纲/于谦)</a></td></tr>

下面是脚本:

#!/bin/sh

num=`ls -l xiangsheng | wc -l`
cnt=1

for wma_file in xiangsheng/*; do
    if [ -f $wma_file ]; then
        echo $cnt/$num
        wma_artist=`./project/parser.py -a $wma_file`
        wma_name=`./project/parser.py -n $wma_file`
        wma_base_name=`basename $wma_file`
        temp_file="temp/"`echo "$wma_base_name" | sed 's/\.\w*$/'.wav'/'`
        out_temp_file="output/"`echo "$wma_base_name" | sed 's/\.\w*$/'.mp3'/'`
        out_file="output/$wma_name"".mp3"

        mplayer -ao pcm:file="$temp_file" "$wma_file"
        lame -m auto --preset phone "$temp_file" "$out_temp_file"
        mv "$out_temp_file" "$out_file"
        id3tag -a"$wma_artist" -s"$wma_name" "$out_file"
        rm "$temp_file"
    fi
    cnt=`expr $cnt + 1`
    echo "\n##############################################3\n"
done

写得丑陋了点,也没有注释。大牛对此当然是不以为意了。

运行脚本,然后过了2个多小时,所有的转换都搞定了。慢慢欣赏相声…

另:今天晚上收到宋波的短信,他今天就要远赴阿尔及利亚去支援第三世界人民建设了。想起我们以前经常在一起说笑打闹,现在突然有最少7年见不到了,感觉有点凄凉。祝他在遥远的阿拉伯国家一切顺利,生活安逸!