当前位置: 首页 > 科技观察

如何将awk脚本移植到Python

时间:2023-03-15 10:46:13 科技观察

将awk脚本移植到Python更多的是关于编码风格而不是转译。脚本是解决问题的有效方式,awk是编写脚本的优秀语言。它特别擅长简单的文本操作,它可以带您完成一些复杂的配置文件重写或目录中文件名的重新格式化。何时从awk切换到Python但在某些方面,awk的局限性开始显现。它没有将文件分解为模块的真正概念,缺乏质量错误报告,并且缺少现在被认为是编程语言如何工作的其他东西。当编程语言的这些丰富特性有助于维护关键脚本时,迁移是一个不错的选择。我最喜欢的具有awk完美端口的现代编程语言是Python。在将awk脚本移植到Python之前,通常值得考虑其原始使用场景。例如,由于awk的局限性,从Bash脚本中调用awk代码是很常见的,其中包括对sed、sort等常用命令行工具的一些调用。最好将所有内容都转换成一个一致的Python程序。有时脚本会做出过于宽泛的假设,例如代码可能允许任意数量的文件,即使实际上只运行一个文件。在考虑了上下文并决定用Python替换什么之后,是时候编写代码了。Python函数的标准awk以下Python函数有助于记住:withopen(some_file_name)asfpin:forlineinfpin:pass#dosomethingwithline此代码将逐行遍历文件并处理行。如果要访问行号(相当于awk的NR),可以使用如下代码:withopen(some_file_name)asfpin:fornr,lineinenumerate(fpin):pass#dosomethingwithlinetoachievemulti-Python中的文件awk-like行为如果您需要能够迭代任意数量的文件,同时保持恒定的行数(如awk的FNR),则此循环可以解决问题:defawk_like_lines(list_of_file_names):def_all_lines():forfilenameinlist_of_file_names:withopen(filename)asfpin:yieldfromfpinyieldfromenumerate(_all_lines())此语法使用Python的生成器和yieldfrom来构建迭代器,迭代所有行并保持持久计数。如果你需要同时使用FNR和NR,这里有一个更复杂的循环:fnr,line)in_all_lines:yieldnr,fnr,lineFNR,NR和linecount更复杂的awk行为如果你需要FNR,NR和linecount这三个都需要,还是会有一些问题。如果是这样,使用三元组(其中两项是数字)会导致混淆。命名参数使这段代码更容易阅读,所以最好使用数据类:filenameinlist_of_file_names:withopen(filename)asfpin:yieldfromenumerate(fpin)fornr,(fnr,line)in_all_lines:yieldAwkLikeLine(nr=nr,fnr=fnr,line=line)你可能想知道,为什么唐你总是用这种方法吗?之所以用其他方法,是因为一直用这个方法太复杂了。如果您的目标是更轻松地将公共库从awk移植到Python,请考虑这样做。但是通常更容易实现和更容易理解(并因此维护)通过编写一个方法来为您提供特定情况下所需的确切循环。了解awk字段一旦有了与一行对应的字符串,如果要转换awk程序,通常需要将其分解为字段。Python有几种方法可以做到这一点。这将用任意数量的连续空格分割该行,返回一个字符串列表:line.split()如果需要另一个字段分隔符,例如用:分隔行,则需要rstrip方法删除最后一个换行符:line。rstrip("\n").split(":")完成以下操作后,列表部分将包含分解后的字符串:parts=line.rstrip("\n").split(":")拆分为非常适合处理参数,但我们处于一个错误的场景中,我们偏离了一个。现在parts[0]将对应awk的$1,parts[1]将对应awk的$2,依此类推。偏移1的原因是awk从1开始计算“字段”,而Python从0开始计数。在awk中,$0是整行——相当于line.rstrip("\n"),awk的NF(字段数)更容易以len(parts)的形式获得。将awk字段移植到Python例如,让我们将这一行“如何使用awk从文件中删除重复行”转换为Python。awk中的原始代码是:awk'!visited[$0]++'your_file>deduplicated_file“真正的”Python翻译是:importcollectionsimportsysvisited=collections.defaultdict(int)forlineinopen("your_file"):did_visit=visited[line]visited[line]+=1ifnotdid_visit:sys.stdout.write(line)但是,Python的数据结构比awk多。为什么不记录访问的行而不是计算访问次数(除了知道我们是否看到一行之外我们不使用它)?importsysvisited=set()forlineinopen("your_file"):iflineinvisited:continuevisited.add(line)sys.stdout.write(line)编写Pythonicawk代码Python社区提倡编写Pythonic代码,这意味着它遵循普遍接受的编码风格。一种更Pythonic的方法会将唯一性和输入/输出问题分开。此更改将使对代码进行单元测试变得更加容易:defunique_generator(things):visited=set()forthinginthings:ifthinginvisited:continuevisited.add(things)yieldthingimportsysforlineinunique_generator(open("your_file")):sys.stdout.write(line)将所有逻辑置于输入/输出代码之外,允许更好地分离关注点并提高代码的可用性和可测试性。结论:在将awk脚本移植到Python时,Python是一个不错的选择,它通常是在考虑适当的Python代码风格的同时重新实现核心需求,而不是通过条件/动作进行笨拙的音译。考虑原始上下文并产生高质量的Python解决方案。虽然使用awk的Bash一行代码有时可以完成工作,但使用Python编码是获得更易于维护的代码的途径。