You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Bash/sed/AWK实现TeX格式花括号解析与转换

用AWK实现TeX格式到Markdown的状态转换

我是数学背景,一直只用Plain TeX做文本编辑,哪怕是非数学内容也不用Word或Google Docs。但偶尔需要提交docx格式文件,之前得把TeX代码粘去Google Docs手动改了再导出,所以在.zshrc里写了个tex2doc的bash函数,用来处理欧洲重音这类简单转换:

tex2doc() {
    (tr '\n' ' ' < $1.tex) |
    sed 's/  /\n\t/g' |
    sed "s/\\\'e/é/g" |
    sed "s/\\\^o/ô/g" |
    sed "s/\\\'E/É/g" |
    sed 's/```/“‘/g' |
    sed "s/'''/’”/g" |
    sed 's/``/“/g' |
    sed "s/''/”/g" |
    sed 's/`/‘/g' |
    sed "s/'/’/g" |
    sed 's/\\\&quot;//g' |
    sed 's/\\~n/ñ/g' |
    sed 's/\~/ /g' |
    sed 's/\\ / /g' |
    sed 's/\\-//g' |
    sed 's/\\c{c}/ç/g' |
    sed 's/\\\///g' |
    sed 's/---/—/g' |
    sed 's/--/–/g' > $1.txt
}

现在还有更复杂的格式转换需求,要把TeX里的{\it ... }斜体、{\bf ... }粗体、{\sc ... }小型大写转成Markdown格式,具体规则:

  1. 遇到{\it 替换为_
  2. 遇到{\bf 替换为**
  3. 遇到{\sc 替换为空字符串
  4. 遇到}时,根据最近的标记对应替换:{\it对应_,{\bf对应**,{\sc对应空,同时要实现栈的弹出逻辑

试过Python词法分析器但觉得没必要,想找bash风格的方案,知道sed处理不了带状态的解析,求AWK的实现思路和方法。


AWK实现方案

AWK可以通过自定义数组模拟栈结构,处理这种需要状态跟踪的替换逻辑。下面是具体的实现代码,可以集成到你的tex2doc函数里:

#!/usr/bin/awk -f

BEGIN {
    # 定义TeX命令到Markdown标记的映射
    cmd_map["\\it "] = "_"
    cmd_map["\\bf "] = "**"
    cmd_map["\\sc "] = ""
}

{
    line = $0
    i = 1
    len = length(line)
    while (i <= len) {
        # 检查是否匹配{\it/{\bf/{\sc开头的命令
        match(line, /^\{\\(it|bf|sc) /, m)
        if (RSTART == 1) {
            # 输出对应的Markdown标记
            printf "%s", cmd_map["\\" m[1] " "]
            # 将当前命令压入栈
            stack[++stack_ptr] = m[1]
            # 跳过匹配的字符长度
            i += RLENGTH
            line = substr(line, RLENGTH + 1)
            len = length(line)
            continue
        }
        # 检查是否遇到闭合的}
        if (substr(line, i, 1) == "}") {
            if (stack_ptr > 0) {
                # 弹出栈顶的命令,输出对应的闭合标记
                cmd = stack[stack_ptr--]
                printf "%s", (cmd == "it" ? "_" : (cmd == "bf" ? "**" : ""))
            } else {
                # 栈为空时直接输出}(处理不匹配的情况)
                printf "}"
            }
            i++
            line = substr(line, 2)
            len = length(line)
            continue
        }
        # 其他字符直接输出,指针后移
        printf "%s", substr(line, i, 1)
        i++
        line = substr(line, 2)
        len = length(line)
    }
    printf "\n"
}

集成到现有函数的方法

修改你的tex2doc函数,把AWK处理步骤加进去:

tex2doc() {
    (tr '\n' ' ' < $1.tex) |
    sed 's/  /\n\t/g' |
    sed "s/\\\'e/é/g" |
    sed "s/\\\^o/ô/g" |
    sed "s/\\\'E/É/g" |
    sed 's/```/“‘/g' |
    sed "s/'''/’”/g" |
    sed 's/``/“/g' |
    sed "s/''/”/g" |
    sed 's/`/‘/g' |
    sed "s/'/’/g" |
    sed 's/\\\&quot;//g' |
    sed 's/\\~n/ñ/g' |
    sed 's/\~/ /g' |
    sed 's/\\ / /g' |
    sed 's/\\-//g' |
    sed 's/\\c{c}/ç/g' |
    sed 's/\\\///g' |
    sed 's/---/—/g' |
    sed 's/--/–/g' |
    awk -f tex-format.awk > $1.txt
}

逻辑说明

  1. 栈结构:用stack数组和stack_ptr指针模拟栈,遇到TeX格式命令时压入栈,遇到}时弹出栈顶元素,根据弹出的命令输出对应的Markdown闭合标记。
  2. 命令匹配:用AWK的match函数检测开头的{\it 、{\bf 、{\sc 命令,匹配成功后替换成对应的Markdown标记并压栈。
  3. 字符遍历:逐字符处理输入内容,确保每个字符都被正确处理,要么替换要么直接输出。

内容的提问来源于stack exchange,提问作者marcelgoh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 19:35:42