使用Unix工具处理文本:搜索并替换不在某些行之间的所有文本
我想在一堆* .org文件上做一些文本处理.我想在每个文件中更改以下内容:
[my description](link) 至 [[link][my description]] , `some text` 至 =some text= , ## some heading 至 ** some heading , *some italics* 至 /some italics/ ,和 **some bold** 至 *some bold* .是的,这是org-mode语法的IS markdown语法.我知道pandoc.需要注意的是我想要进行上述更改,除非它们出现在以下块中: #+BEGIN_EXAMPLE don't want above changes to take place in this block ... #+END_EXAMPLE 因此,我不能使用pandoc.我想根据上述要求使用某种unix脚本处理这些文件:awk,sed,python,perl,bash等.一旦我有了一个工作脚本,我就可以修改它并从中学习. 谢谢你的帮助! 解决方法
Perl解决方案
这是我为@ jkerian脚本建议的简化更改的结果:使用触发器操作符和-p.我还修复了他的正则表达式,在RHS中使用正确的$1和$2,从s ///到s :::改变分隔符以避免LTS(“Leaning Toothpick Syndrome”),并添加/ x以提高可读性.在处理粗体和斜体时存在逻辑错误,我已经修复了.我添加了注释,显示每种情况下的转换应该与原始问题描述相对应,并对齐转换的RHS以使它们更易于阅读. #!/usr/bin/perl -p # # the -p option makes this a pass-through filter ##################################################### # omit protected region next if /^#+BEGIN_EXAMPLE/ .. /^#+END_EXAMPLE/; # `some text` ? =some text= s: ` ( [^`]* ) ` :=$1=:gx; # [desc](link) ? [[link][desc]] s: [ ( [^]]* ) ] ( ( [^)]* ) ) :[[$2][$1]]:gx; # ^## some heading ? ** some heading # NB: can't use /x here or would have to use ugly # s:^##:**:; # *some italics* ? /some italics/ s: (?!< * ) * ( [^*]+ ) * (?! *) :/$1/:gx; # **some bold** ? *some bold* s: *{2} ( [^*]+ ) *{2} :*$1*:gx; 看看这有多容易? Perl中只有6行简单易读的代码.在Perl中很容易,因为Perl专门用于编写这种过滤器非常简单,而Python则不然. Python有独立的设计目标. 虽然你当然可以在Python中重写它,但是不值得这么麻烦,因为Python根本就不是为这类东西而设计的. Python缺少-p“make-me-a-filter”标志,用于隐式循环和隐式打印. Python缺少隐式累加器变量. Python缺少内置的正则表达式. Python缺少s ///运算符. Python缺少有状态的翻转操作符.所有这些都有助于使Perl解决方案比Python解决方案更容易阅读,编写和维护. 但是,你不应该认为这总是存在的.它没有.在其他领域,您可以提出Python在这些方面领先的问题.但不是在这里.这是因为这个过滤器是Perl专注的专业领域,而不是Python. 因此,与简单的Perl版本相比,Python解决方案将更长,更嘈杂,更难以阅读 – 因此更难维护 – 因为Perl旨在简化操作,这是其目标应用领域之一.尝试用Python重写它,并注意它是多么令人讨厌.当然有可能,但不值得麻烦,或维护噩梦. Python版本 #!/usr/bin/env python3.2 from __future__ import print_function import sys import re if (sys.version_info[0] == 2): sys.stderr.write("%s: legacy Python detected! Please upgrade to v3+n" % sys.argv[0] ) ##sys.exit(2) if len(sys.argv) == 1: sys.argv.append("/dev/stdin") flip_rx = re.compile(r'^#+BEGIN_EXAMPLE') flop_rx = re.compile(r'^#+END_EXAMPLE') #EG# `some text` --> =some text= lhs_backticks = re.compile(r'` ( [^`]* ) `',re.VERBOSE) rhs_backticks = r'=1=' #EG# [desc](link) --> [[link][desc]] lhs_desclink = re.compile(r' [ ( [^]]* ) ] ( ( [^)]* ) ) ',re.VERBOSE) rhs_desclink = r'[[2][1]]' #EG# ^## some heading --> ** some heading lhs_header = re.compile(r'^##') rhs_header = r'**' #EG# *some italics* --> /some italics/ lhs_italics = re.compile(r' (?!< * ) * ( [^*]+ ) * (?! *) ',re.VERBOSE) rhs_italics = r'/1/' ## **some bold** --> *some bold* lhs_bold = re.compile(r'*{2} ( [^*]+ ) *{2}',re.VERBOSE) rhs_bold = r'*1*' errcnt = 0 flipflop = "flip" for filename in sys.argv[1:]: try: filehandle = open(filename,"r") except IOError as oops: errcnt = errcnt + 1 sys.stderr.write("%s: can't open '%s' for reading: %sn" % ( sys.argv[0],filename,oops) ) else: try: for line in filehandle: new_flipflop = None if flipflop == "flip": if flip_rx.search(line): new_flipflop = "flop" elif flipflop == "flop": if flop_rx.search(line): new_flipflop = "flip" else: raise FlipFlop_SNAFU if flipflop != "flop": line = lhs_backticks . sub ( rhs_backticks,line) line = lhs_desclink . sub ( rhs_desclink,line) line = lhs_header . sub ( rhs_header,line) line = lhs_italics . sub ( rhs_italics,line) line = lhs_bold . sub ( rhs_bold,line) print(line,end="") if new_flipflop != None: flipflop = new_flipflop except IOError as oops: errcnt = errcnt + 1 sys.stderr.write("%s: can't read '%s': %sn" % ( sys.argv[0],oops) ) finally: try: filehandle.close() except IOError as oops: errcnt = errcnt + 1 sys.stderr.write("%s: can't close '%s': %sn" % ( sys.argv[0],oops) ) if errcnt == 0: sys.exit(0) else: sys.exit(1) 摘要 使用正确的工具来完成正确的工作非常重要.对于此任务,该工具是Perl,它只占用了7行.只有7件事要做,但不要试着告诉Python.这就像回到具有太多中断堆栈的汇编语言一样.对于这种工作来说,72行的Python显然没有被删除,所有令人痛苦的复杂性和噪音不可读的代码都显示了你究竟为什么.无论语言如何,每行代码的错误率都是相同的,因此如果您在编写N行代码或10 * N行代码之间做出选择,则别无选择. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |