har*_*on4 2 python regex bash perl awk
我正在尝试替换像这种模式的行块:
让我们看一个例子,这个输入:
01 hello
02 stack
02 overflow
04 hi
02 friends = overflow
03 this
03 is
03 my = is
03 life
02 lol
02 im
02 joking = im
03 filler
Run Code Online (Sandbox Code Playgroud)
将生成以下输出(每个hello块是数组的一个元素):
01 hello
02 stack
02 overflow
04 hi
02 lol
02 im
01 hello
02 stack
02 overflow
04 hi
02 lol
02 joking = im
03 filler
01 hello
02 stack
02 friends = overflow
03 this
03 is
03 life
02 lol
02 im
01 hello
02 stack
02 friends = overflow
03 this
03 is
03 life
02 lol
02 joking = im
03 filler
01 hello
02 stack
02 friends = overflow
03 this
03 my = is
03 life
02 lol
02 im
01 hello
02 stack
02 friends = overflow
03 this
03 my = is
03 life
02 lol
02 joking = im
03 filler
Run Code Online (Sandbox Code Playgroud)
我通过这种方式尝试了它:
#!/bin/bash
awk '{
if ($0~/=/){
level=$1
oc=1
}else if (oc && $1<=level){
oc=0
}
if (!oc){
print
}
}' input.txt
Run Code Online (Sandbox Code Playgroud)
但它只返回我需要的第一个输出,而且我不知道如何跳过其中的03 life
单词friends
.
我怎么能产生这些输出?
我不介意python或perl解决方案,如果你更舒适.
这是一个python脚本,用于读取cobol输入文件并打印出已定义和重新定义的变量的所有可能组合:
#!/usr/bin/python
"""Read cobol file and print all possible redefines."""
import sys
from itertools import product
def readfile(fname):
"""Read cobol file & return a master list of lines and namecount of redefined lines."""
master = []
namecount = {}
with open(fname) as f:
for line in f:
line = line.rstrip(' .\t\n')
if not line:
continue
words = line.split()
n = int(words[0])
if '=' in words or 'REDEFINES' in words:
name = words[3]
else:
name = words[1]
master.append((n, name, line))
namecount[name] = namecount.get(name, 0) + 1
# py2.7: namecount = {key: val for key, val in namecount.items() if val > 1}
namecount = dict((key, val) for key, val in namecount.items() if val > 1)
return master, namecount
def compute(master, skip=None):
"""Return new cobol file given master and skip parameters."""
if skip is None:
skip = {}
seen = {}
skip_to = None
output = ''
for n, name, line in master:
if skip_to and n > skip_to:
continue
seen[name] = seen.get(name, 0) + 1
if seen[name] != skip.get(name, 1):
skip_to = n
continue
skip_to = None
output += line + '\n'
return output
def find_all(master, namecount):
"""Return list of all possible output files given master and namecount."""
keys = namecount.keys()
values = [namecount[k] for k in keys]
out = []
for combo in product(*[range(1, v + 1) for v in values]):
skip = dict(zip(keys, combo))
new = compute(master, skip=skip)
if new not in out:
out.append(new)
return out
def main(argv):
"""Process command line arguments and print results."""
fname = argv[-1]
master, namecount = readfile(fname)
out = find_all(master, namecount)
print('\n'.join(out))
if __name__ == '__main__':
main(sys.argv)
Run Code Online (Sandbox Code Playgroud)
如果上面的脚本保存在一个名为的文件中cobol.py
,那么如果可以运行为:
python cobol.py name_of_input_file
Run Code Online (Sandbox Code Playgroud)
定义和重新定义的各种可能组合将显示在stdout上.
此脚本在python2(2.6+)或python3下运行.
该代码使用三个函数:
readfile
读取输入文件并返回两个变量,这些变量汇总了其中的结构.
compute
从中获取两个参数,计算输出块.
find_all
确定所有可能的输出块,用于compute
创建它们,然后将它们作为列表返回.
让我们更详细地看一下每个函数:
readfile
readfile
将输入文件名作为参数并返回列表master
和字典namecount
.对于输入文件中的每个非空行,列表master
都有一个元组,其中包含(1)级别编号,(2)定义或重新定义的名称,以及(2)原始行本身.对于示例输入文件,readfile
返回以下值master
:
[(1, 'hello', '01 hello'),
(2, 'stack', ' 02 stack'),
(2, 'overflow', ' 02 overflow'),
(4, 'hi', ' 04 hi'),
(2, 'overflow', ' 02 friends = overflow'),
(3, 'this', ' 03 this'),
(3, 'is', ' 03 is'),
(3, 'is', ' 03 my = is'),
(3, 'life', ' 03 life'),
(2, 'lol', ' 02 lol'),
(2, 'im', ' 02 im'),
(2, 'im', ' 02 joking = im'),
(3, 'filler', ' 03 filler')]
Run Code Online (Sandbox Code Playgroud)
readfile
还返回字典namecount
,该字典具有重新定义的每个名称的条目,并且具有该名称的定义/重新定义的数量.对于示例输入文件,namecount
具有以下值:
{'im': 2, 'is': 2, 'overflow': 2}
Run Code Online (Sandbox Code Playgroud)
这表明im
,is
和overflow
每一个有两个可能的值.
readfile
当然是设计用于在当前版本的问题中使用输入文件格式.在可能的范围内,它也被设计为使用此问题的先前版本的格式.例如,无论是使用等号(当前版本)还是使用REFDEFINES
以前版本中的单词发信号,都可以接受变量重新定义.这旨在使此脚本尽可能灵活.
compute
该函数compute
是生成每个输出块的函数.它使用两个参数.第一个是master
直接来自readfile
.第二个是skip
从namecount
返回的字典派生的readfile
.例如,namecount
字典表示有两种可能的定义im
.这显示了如何compute
使用它们为每个生成输出块:
In [14]: print compute(master, skip={'im':1, 'is':1, 'overflow':1})
01 hello
02 stack
02 overflow
04 hi
02 lol
02 im
In [15]: print compute(master, skip={'im':2, 'is':1, 'overflow':1})
01 hello
02 stack
02 overflow
04 hi
02 lol
02 joking = im
03 filler
Run Code Online (Sandbox Code Playgroud)
观察到对compute
上面的第一次调用生成了使用第一个定义im
的块,第二个调用生成了使用第二个定义的块.
find_all
有了上述两个功能,很明显最后一步只是生成所有不同的定义组合并将其打印出来.这就是功能的find_all
作用.使用master
和namecount
返回时readfile
,系统会运行所有可用的定义和调用组合,compute
为每个组合创建一个块.它收集所有可以通过这种方式创建的独特块并返回它们.
返回的输出find_all
是字符串列表.每个字符串都是对应于define/redefines的一个组合的块.使用问题的示例输入,显示find_all
返回的内容:
In [16]: find_all(master, namecount)
Out[16]:
['01 hello\n 02 stack\n 02 overflow\n 04 hi\n 02 lol\n 02 im\n',
'01 hello\n 02 stack\n 02 friends = overflow\n 03 this\n 03 is\n 03 life\n 02 lol\n 02 im\n',
'01 hello\n 02 stack\n 02 overflow\n 04 hi\n 02 lol\n 02 joking = im\n 03 filler\n',
'01 hello\n 02 stack\n 02 friends = overflow\n 03 this\n 03 is\n 03 life\n 02 lol\n 02 joking = im\n 03 filler\n',
'01 hello\n 02 stack\n 02 friends = overflow\n 03 this\n 03 my = is\n 03 life\n 02 lol\n 02 im\n',
'01 hello\n 02 stack\n 02 friends = overflow\n 03 this\n 03 my = is\n 03 life\n 02 lol\n 02 joking = im\n 03 filler\n']
Run Code Online (Sandbox Code Playgroud)
作为一个例子,让我们采取返回的第四个字符串find_all
,为了更好的格式,我们将print
它:
In [18]: print find_all(master, namecount)[3]
01 hello
02 stack
02 friends = overflow
03 this
03 is
03 life
02 lol
02 joking = im
03 filler
Run Code Online (Sandbox Code Playgroud)
在完整的脚本中,输出find_all
组合在一起并打印到stdout,如下所示:
out = find_all(master, namecount)
print('\n'.join(out))
Run Code Online (Sandbox Code Playgroud)
这样,输出显示所有可能的块.
awk 'f==0 && !/REDEFINES/{s=s"\n"$0;next} /REDEFINES/{f=1;print s t>("output" ++c ".txt");t=""} {t=t"\n"$0} END{print s t>("output" ++c ".txt")}' input
Run Code Online (Sandbox Code Playgroud)
该程序具有以下变量:
f
是一个标志,在第一个REDEFINE之前为零,之后为一个.
s
包含第一个REDEFINE之前的所有文本.
t
包含当前REDEFINE的文本.
c
是一个计数器,用于确定输出名称的名称.
代码的工作原理如下:
f==0 && !/REDEFINES/{s=s"\n"$0;next}
在遇到第一次重新定义之前,文本保存在变量中s
,我们跳过其余的命令并跳转到该next
行.
/REDEFINES/{f=1;print s t>("output" ++c ".txt");t=""}
每次遇到REDEFINE行时,我们将标志设置f
为1,并将prolog部分s
与当前REDEFINE部分一起打印到一个名为outputn.txt
n 的文件,其中n由计数器的值替换c
.
因为我们处于新的REDEFINE部分的开头,所以该变量t
设置为空.
{t=t"\n"$0}
将此REDEFINE的当前行保存到变量中t
.
END{print s t>("output" ++c ".txt")}
打印最后一个REDEFINE部分的输出文件.
上面代码生成的每个输出文件都有一个前导空白行.下面的代码通过awk
substr
函数删除:
awk '/REDEFINES/{f=1;print substr(s,2) t>("output" ++c ".txt");t=""} f==0 {s=s"\n"$0;next} {t=t"\n"$0} END{print substr(s,2) t>("output" ++c ".txt")}' input
Run Code Online (Sandbox Code Playgroud)
对于多样性,此版本的逻辑略有不同,但是,否则会获得相同的结果.
awk 'f==1 && pre==$1 && !/REDEFINES/{tail=tail "\n" $0} /REDEFINES/{pre=$1;f=1;t[++c]="\n"$0} f==0 {head=head"\n"$0;next} pre!=$1{t[c]=t[c]"\n"$0} END{for (i=0;i<=c;i++) {print head t[i] tail>("output" (i+1) ".txt")}}' file
Run Code Online (Sandbox Code Playgroud)