我希望对一堆* .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等。一旦我有了一个工作脚本,我就可以修改它并从中学习。
感谢您的帮助!
答案 0 :(得分:6)
这是我为@ jkerian脚本建议的简化更改的结果:使用触发器操作符和-p
。我还修复了他的正则表达式,在RHS中使用了正确的$1
和$2
,修改了从s///
到s:::
的分隔符以避免LTS (“Leaning Toothpick综合症“),并添加/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。
Python解决方案因此比这个简单的Perl版本更长,更嘈杂,更难以阅读 - 因此更难维护 - 因为Perl旨在简化操作,这是其目标应用之一区域。尝试用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: %s\n"
% ( 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': %s\n"
% ( sys.argv[0], filename, oops) )
finally:
try:
filehandle.close()
except IOError as oops:
errcnt = errcnt + 1
sys.stderr.write("%s: can't close '%s': %s\n"
% ( sys.argv[0], filename, oops) )
if errcnt == 0:
sys.exit(0)
else:
sys.exit(1)
使用合适的工具来完成正确的工作非常重要。对于此任务,该工具是Perl,它只占用了7行。只有7件事要做,但不要试着告诉Python。这就像回到具有太多中断堆栈的汇编语言一样。对于这种工作来说,72行的Python显然没有被删除,所有令人痛苦的复杂性和噪音不可读的代码都显示了你究竟为什么。无论语言如何,每行代码的错误率是相同的,所以如果您在编写N行代码或10 * N行代码之间做出选择,则别无选择。
答案 1 :(得分:4)
我认为您正在寻找类似以下perl脚本的内容
while(<>) {
if /#\+BEGIN_EXAMPLE/ .. /#\+END_EXAMPLE/ {
print;
next;
}
s/`([^`]*)`/=\1=/g;
s/\[([^]]*)\]\(([^)]*)\)/[[\2][\1]]/g;
s/^##/**/;
s/\*([^\*]+)\*/\/\1\//g;
s/\*\/([^\/]+)\/\*/*\1*/g;
print;
}
使用cat testfile | perl scriptname.pl
对于非傻版本的python。注意:Perl是适合这项工作的工具,但是tchrist的python版本是一个糟糕的笑话,必须修复它。
from __future__ import print_function
import fileinput
import re
import sys
sys.tracebacklimit=0 #For those desperate to hide tracebacks in one-off scripts
example = 0
for line in fileinput.input():
if example==0 and re.match(r'^#\+BEGIN_EXAMPLE',line):
example+=1
elif example>=1:
if re.match(r'^#\+END_EXAMPLE',line): example-=1
else:
line = re. sub (r'` ( [^`]* ) `', r'=\1=', line, 0, re.VERBOSE)
line = re. sub (r'\[ ( [^]]* ) \] \( ( [^)]* ) \) ', r'[[\2][\1]]', line, 0, re.VERBOSE)
line = re. sub (r'^\#\#', r'**', line, 0, re.VERBOSE)
line = re. sub (r'(?!< \* ) \* ( [^*]+ ) \* (?! \*)', r'/\1/', line, 0, re.VERBOSE)
line = re. sub (r'\*{2} ( [^*]+ ) \*{2}', r'*\1*', line, 0, re.VERBOSE)
print(line, end="")
答案 2 :(得分:1)
只是为了笑容,这是我的python解决方案版本:
from __future__ import print_function
import fileinput, functools, re, sys
# For those desperate to hide tracebacks in one-off scripts
sys.tracebacklimit = 0
# Precompile all our patterns for speed
begin_example = re.compile(r'^#\+BEGIN_EXAMPLE').match
end_example = re.compile(r'^#\+END_EXAMPLE').match
# Use partial to eliminate lookups inside our loop
fixes = [ functools.partial(re.compile(x[0], x[2]).sub, x[1]) for x in
(r'` ( [^`]* ) `', r'=\1=', re.VERBOSE),
(r'\[ ( [^]]* ) \] \( ( [^)]* ) \) ', r'[[\2][\1]]', re.VERBOSE),
(r'^\#\#', r'**', re.VERBOSE),
(r'(?!< \* ) \* ( [^*]+ ) \* (?! \*)', r'/\1/', re.VERBOSE),
(r'\*{2} ( [^*]+ ) \*{2}', r'*\1*', re.VERBOSE),
]
inside = False
for line in fileinput.input():
if inside:
if end_example(line):
inside = False
else:
if begin_example(line):
inside = True
for fixup in fixes:
line = fixup(line)
print(line, end='')