如何将 awk 脚本移植到 Python

作者:Moshe Zadka 时间:2022-02-28 05:40:52 

将一个 awk 脚本移植到 Python 主要在于代码风格而不是转译。

脚本是解决问题的有效方法,而 awk 是编写脚本的出色语言。它特别擅长于简单的文本处理,它可以带你完成配置文件的某些复杂重写或目录中文件名的重新格式化。

何时从 awk 转向 Python

但是在某些方面,awk 的限制开始显现出来。它没有将文件分解为模块的真正概念,它缺乏质量错误报告,并且缺少了现在被认为是编程语言工作原理的其他内容。当编程语言的这些丰富功能有助于维护关键脚本时,移植将是一个不错的选择。

我最喜欢的完美移植 awk 的现代编程语言是 Python。

在将 awk 脚本移植到 Python 之前,通常值得考虑一下其原始使用场景。例如,由于 awk 的局限性,通常从 Bash 脚本调用 awk 代码,其中包括一些对 sed、sort 之类的其它命令行常见工具的调用。 最好将所有内容转换为一个一致的 Python 程序。有时,脚本会做出过于宽泛的假设,例如,即使实际上只运行一个文件,该代码也可能允许任意数量的文件。

在仔细考虑了上下文并确定了要用 Python 替代的东西之后,该编写代码了。

标准 awk 到 Python 功能

以下 Python 功能是有用的,需要记住:


with open(some_file_name) as fpin:
 for line in fpin:
   pass # do something with line

此代码将逐行循环遍历文件并处理这些行。

如果要访问行号(相当于 awk 的 NR),则可以使用以下代码:


with open(some_file_name) as fpin:
 for nr, line in enumerate(fpin):
   pass # do something with line

在 Python 中实现多文件的 awk 式行为

如果你需要能够遍历任意数量的文件同时保持行数的持续计数(类似 awk 的 FNR),则此循环可以做到这一点:


def awk_like_lines(list_of_file_names):
 def _all_lines():
   for filename in list_of_file_names:
     with open(filename) as fpin:
       yield from fpin
 yield from enumerate(_all_lines())

此语法使用 Python 的生成器和 yield from 来构建迭代器,该迭代器将遍历所有行并保持一个持久计数。

如果你需要同时使用 FNR 和 NR,这是一个更复杂的循环:


def awk_like_lines(list_of_file_names):
 def _all_lines():
   for filename in list_of_file_names:
     with open(filename) as fpin:
       yield from enumerate(fpin)
 for nr, (fnr, line) in _all_lines:
   yield nr, fnr, line

更复杂的 FNR、NR 和行数的 awk 行为

如果 FNR、NR 和行数这三个你全都需要,仍然会有一些问题。如果确实如此,则使用三元组(其中两个项目是数字)会导致混淆。命名参数可使该代码更易于阅读,因此最好使用 dataclass:


import dataclass
@dataclass.dataclass(frozen=True)
class AwkLikeLine:
 content: str
 fnr: int
 nr: int
def awk_like_lines(list_of_file_names):
 def _all_lines():
   for filename in list_of_file_names:
     with open(filename) as fpin:
       yield from enumerate(fpin)
 for nr, (fnr, line) in _all_lines:
   yield AwkLikeLine(nr=nr, fnr=fnr, line=line)

你可能想知道,为什么不一直用这种方法呢?使用其它方式的的原因是总用这种方法太复杂了。如果你的目标是把一个通用库更容易地从 awk 移植到 Python,请考虑这样做。但是编写一个可以使你确切地了解特定情况所需的循环的方法通常更容易实现,也更容易理解(因而易于维护)。

理解 awk 字段

一旦有了与一行相对应的字符串,如果要转换 awk 程序,则通常需要将其分解为字段。Python 有几种方法可以做到这一点。这将把行按任意数量的连续空格拆分,返回一个字符串列表:

line.split()

如果需要另一个字段分隔符,比如以 : 分隔行,则需要 rstrip 方法来删除最后一个换行符:

line.rstrip("\n").split(":")

完成以下操作后,列表 parts 将存有分解的字符串:

parts = line.rstrip("\n").split(":")

这种拆分非常适合用来处理参数,但是我们处于偏差一个的错误场景中。现在 parts[0] 将对应于 awk 的 $1,parts[1] 将对应于 awk 的 $2,依此类推。之所以偏差一个,是因为 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 转换将是:

import collections
import sys
visited = collections.defaultdict(int)
for line in open("your_file"):
 did_visit = visited[line]
 visited[line] += 1
 if not did_visit:
   sys.stdout.write(line)

但是,Python 比 awk 具有更多的数据结构。与其计数访问次数(除了知道是否看到一行,我们不使用它),为什么不记录访问的行呢?


import sys
visited = set()
for line in open("your_file"):
 if line in visited:
   continue
 visited.add(line)
 sys.stdout.write(line)

编写 Python 化的 awk 代码

Python 社区提倡编写 Python 化的代码,这意味着它要遵循公认的代码风格。更加 Python 化的方法将区分唯一性和输入/输出的关注点。此更改将使对代码进行单元测试更加容易:


def unique_generator(things):
 visited = set()
 for thing in things:
   if thing in visited:
     continue
   visited.add(things)
   yield thing
import sys

for line in unique_generator(open("your_file")):
 sys.stdout.write(line)

将所有逻辑置于输入/输出代码之外,可以更好地分离问题,并提高代码的可用性和可测试性。

结论:Python 可能是一个不错的选择

将 awk 脚本移植到 Python 时,通常是在考虑适当的 Python 代码风格时重新实现核心需求,而不是按条件/操作进行笨拙的音译。考虑原始上下文并产生高质量的 Python 解决方案。虽然有时候使用 awk 的 Bash 单行代码可以完成这项工作,但 Python 编码是通往更易于维护的代码的途径。

总结

以上所述是小编给大家介绍的如何将 awk 脚本移植到 Python网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

来源:https://linux.cn/article-11654-1.html

标签:awk,脚本,python
0
投稿

猜你喜欢

  • 利于python脚本编写可视化nmap和masscan的方法

    2021-04-14 11:19:52
  • python使用openpyxl库读写Excel表格的方法(增删改查操作)

    2021-11-29 01:22:43
  • Python类的基础入门知识

    2021-07-09 04:07:25
  • 如何用ASP建立Index Server查询对象并为其参数赋值?

    2010-06-11 19:59:00
  • Python Opencv轮廓常用操作代码实例解析

    2023-01-03 08:46:59
  • python和pygame实现简单俄罗斯方块游戏

    2022-10-04 20:00:49
  • Python 正则表达式详解

    2021-12-03 11:11:38
  • [ASP]提高数据显示效率--缓存探幽

    2008-05-18 13:51:00
  • 美图秀秀web开放平台--PHP流式上传和表单上传示例分享

    2023-11-15 08:34:42
  • 常用的匹配正则表达式和实例

    2008-06-07 09:19:00
  • 倾斜的鼠标翻转导航制作上的烦恼

    2007-06-20 16:39:00
  • Python理解递归的方法总结

    2022-06-10 03:31:08
  • 解决Pandas to_json()中文乱码,转化为json数组的问题

    2023-12-05 19:06:33
  • Django框架中间件(Middleware)用法实例分析

    2021-01-04 09:27:38
  • 在Python中编写数据库模块的教程

    2024-01-22 21:28:40
  • mysql登录遇到ERROR 1045问题解决方法

    2024-01-22 15:09:57
  • 一文搞懂MySQL元数据锁(MDL)

    2024-01-14 18:29:08
  • Python-pip配置国内镜像源的安装方式

    2023-08-22 21:55:38
  • Python插件机制实现详解

    2021-08-28 06:55:30
  • MySQL数据库卸载的完整步骤

    2024-01-13 13:12:52
  • asp之家 网络编程 m.aspxhome.com