通过实例掌握流编辑器sed

通过实例掌握流编辑器sed

2020-01-08
| 杂事 | | sed , latex , hugo , typora | Comment 评论

缘由

由于Hugo对Markdown的解析和LaTeX存在诸多冲突。比如:当LaTex中出现\\\{\}之类的东西,Hugo就无法正常解析数学公式。

解决方案之一就是用Hugo的shortcodes机制,在md文件中用形如{{< katex [display] >}}latex code{{< /katex >}}格式。

这样一来,网站显示正常了,但在本地Typora就无法识别了。 个人体验不好。

于是我想到用流编编器sed对md内容进行替换。 我以前用sed都是简单的情况,复杂需求我就不会用了。 于是有了此文。

需求

利用Hugo发布到站点前:将$latex code$,置换成 {{< katex [display] >}}latex code{{< /katex >}}

Typora写作编辑前: 将 {{< katex [display] >}}latex code{{< /katex >}},置换成 $latex code$

注意:置换时,同时要跳过代码块不处理。

我写成的代码(后面将逐项解释,备忘)

#!/bin/bash

# md文件递归
function action(){
    for file in `ls $1` 
    do
        if [ -d $1"/"$file ] 
        then
            action $1"/"$file
        elif [ "${file#*.}"x = "md"x ]
        then
            # 方便发布到gitee
            sed -i '/```/ { :begin1; /```.*```/! { $! { N; b begin1 }; }; n; }; /\$\$/ { :begin2; /\$\$[^$]*\$\$/! { $! { N; b begin2 }; }; }; s/\$\$\(\n[^$]*\n\)\$\$/{{< katex display >}}\1{{< \/katex >}}/g; s/\$\$\([^$]*\)\$\$/{{< katex >}}\1{{< \/katex >}}/g; s/!\[.*\](\.\.\/images\//![](..\/..\/images\//g;'  $1"/"$file
        fi
    done
} 

action ./content

if [ "$1"x = "server"x ] 
then
	# 站点本地预览
    hugo server --disableFastRender -D
else
	# 站点静态文件生成
    hugo -D
    # 文件恢复成Typora可正常阅读状态
    ./fortypora
fi

相关SED代码的详细解释(备忘)

把上面代码中SED部分单独提出来,写成方便阅读的形式,并逐行解释:

# 正则识别代码块的开始界定符
/```/ {     
	:begin1
	# 正则识别完整代码块
	/```.*```/! { 
		# 没能匹配代码块,则读取下行,附加到“模式空间”
		$! { N; b begin1 } 
	} 
	# 成功匹配代码块,则读取下行,清空“模式空间”
	n; 
} # 上面这段代码目的: 跳过代码块```code```

# 正则识别标准数学公式开始界定符
/\$\$/ { 
	:begin2 
	# 正则识别完整的数学公式块(界定符)
	/\$\$[^$]*\$\$/! { 
		# 没能数学公式块,则读取下行,附加到“模式空间”
		$! { N; b begin2 } 
	} 
} 
# 对“(多行)模式空间” 进行置换:块公式的界定符变换
s/\$\$\(\n[^$]*\n\)\$\$/{{< katex display >}}\1{{< \/katex >}}/g
# 对“(多行)模式空间” 进行置换:内联公式的界定符变换
s/\$\$\([^$]*\)\$\$/{{< katex >}}\1{{< \/katex >}}/g
# 对“(多行)模式空间” 进行置换:顺带进行链接变换
s/!\[.*\](\.\.\/images\//![](..\/..\/images\//g

上面的注释已经解释的很清楚了:

  • 展示了如何用SED进行多行匹配:先识别开始界定符,然后反复检查代码块的完整性,不完整则逐行添加,完整则继续。
  • 这里只用到了"模式空间"。命令N可将”单行模式空间“扩展成”多行模式空间“;命令n则将”多行模式空间“清空,恢复成”单行模式空间“。
  • 最后通过s/<正则匹配被替换老的部分>/<新的部分>/g,完成置换。
  • 注意: 把多行压缩时需要添加; (注意后面有个空格)。

一旦理解这个例子,我想一般的SED就不在话下。