Vin*_*yen 4 python bash perl awk sed
我想在一堆*.org文件上做一些文本处理.我想在每个文件中更改以下内容:
[my description](link)
Run Code Online (Sandbox Code Playgroud)
至
[[link][my description]]
Run Code Online (Sandbox Code Playgroud)
,
`some text`
Run Code Online (Sandbox Code Playgroud)
至
=some text=
Run Code Online (Sandbox Code Playgroud)
,
## some heading
Run Code Online (Sandbox Code Playgroud)
至
** some heading
Run Code Online (Sandbox Code Playgroud)
,
*some italics*
Run Code Online (Sandbox Code Playgroud)
至
/some italics/
Run Code Online (Sandbox Code Playgroud)
,和
**some bold**
Run Code Online (Sandbox Code Playgroud)
至
*some bold*
Run Code Online (Sandbox Code Playgroud)
.是的,这是org-mode语法的IS markdown语法.我知道pandoc.需要注意的是,我想要进行上述更改,除非它们出现在以下块中:
#+BEGIN_EXAMPLE
don't want above changes to take place in this block
...
#+END_EXAMPLE
Run Code Online (Sandbox Code Playgroud)
因此,我不能使用pandoc.我想根据上述要求使用某种unix脚本处理这些文件:awk,sed,python,perl,bash等.一旦我有了一个工作脚本,我就可以修改它并从中学习.
谢谢你的帮助!
这是我为@ jkerian脚本建议的简化更改的结果:使用触发器操作符和-p.我也固定他的正则表达式使用正确的$1,并$2在RHS,从改变分隔符s///,以s:::避免LTS ("倾斜牙签综合症") ,并增加/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;
Run Code Online (Sandbox Code Playgroud)
看看这有多容易?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重写它,并注意它是多么令人讨厌.当然有可能,但不值得麻烦,或维护噩梦.
#!/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)
Run Code Online (Sandbox Code Playgroud)
使用正确的工具来完成正确的工作非常重要.对于此任务,该工具是Perl,它只占用了7行.只有7件事要做,但不要试着告诉Python.这就像回到具有太多中断堆栈的汇编语言一样.对于这种工作来说,72行的Python显然没有被删除,所有令人痛苦的复杂性和噪音不可读的代码都显示了你究竟为什么.无论语言如何,每行代码的错误率都是相同的,因此如果您在编写N行代码或10*N行代码之间做出选择,则别无选择.