bash这么“古老”的东西从很久以前就支持这么“新潮”的概念, 我承认我一度惊呆了。
元编程?
如果你知道这是神马,可以跳过这节内容;如果你不知道这 是神马,看完下面这句话之后也可以跳过这节内容: 元编程就是把程序作为数据处理的过程。
OK这节内容就到此为止。
问题
今天在公司打酱油的时候被逮住了,某人说他想要这么个功 能:替换指定文本文件里的若干个指定模式——像把abc和def 分别替换成uvw和xyz——并且在我有机会想“妹的不会自己写 啊”之前就大方承认他妹的真的不会写。
哥,不会写没关系,但是你确定我帮你写出来之后你能看明 白吗……
解决方案
好吧用bash脚本写字符串算法不是不可以,但是这种行为学 名叫“作死”,我们并不提倡;直接使用各种文本处理命令会 比较上道。
说到“替换”字符串,第一个反应是sed
;要替换很多个字
符串,第一个反应就是很多个sed
……至于怎么搞出很多个
sed
来呢,鉴于本人是专业写Erlang的,用循环什么的就
弱爆了——这里用的是管道,具体可以拆解成两个步骤:
- 对每个要被替换的模式,生成执行替换的bash函数
- 用管道将所有替换函数连接起来
例如,我们定义了函数subst_func_1
,它将标准输入流
里的abc替换成uvw,另一个函数subst_func_2
则是将def
替换成xyz;要“同时”替换abc和def,只要将这两个函数用
管道连接到一起就可以了:
1 |
|
问题是,subst_func_1
这样的函数要怎么定义?写死在
脚本里?那难道有多少个模式要替换就要写多少个函数?
当然不可能。鉴于替换函数的结构基本上是一样的,我们可
以在eval
后面写一个这样的模板:
1 2 3 4 |
|
然后只要提供name
、from
、to
这三个变量就万事俱
备了。没错这个模板的替换和解析过程就是前面算是提到
过的“把程序作为数据处理的过程”——元编程。
说实话我是写完这段代码才意识到它能被看作元编程的, bash这么“古老”的东西从很久以前就支持这么“新潮”的概 念,我承认我一度惊呆了。
语言的元编程特性
- 其实所有编程语言都能做元编程,只不过内置元编程 支持的语言一般不需要显式调用自己的编译器(解释 器),而是提供了其他更方便、更安全的元编程接口;
- 所有类似
eval
的操作其实都是元编程接口; - 元编程的核心步骤是将数据转换为程序执行。
完整程序
关于bash的子进程
写这个东西的时候遇到一个“奇怪”的问题,以前重来没 有留意过;如果将程序的36行改成这样:
1 |
|
Shell会抱怨找不到subst_func_*
函数,这是因为bash会
fork一个子进程用来执行括号中的命令,所以gen_subst_func
建立的subst_func_*
函数存在于子进程中,gen_subst_func
函数一返回就消失了——这里有详细说明。