Python调用os模块执行sed命令时引号转义冲突问题求解
问题背景
从指定域名爬取得到大量端点结果,数据以JSON格式存储。此前已通过sed命令聚合JSON内容提取端点,现需要将所有sed命令整合到Python爬虫脚本中,爬取输出样例如下:
{'Results': [{'Result': {'IsDB': 'True', 'Spend': 367, 'Paths': [{'Technologies': [{'Categories': ['Japan hosting'], 'Name': 'Internet Initiative Japan','Link': 'https://www.iij.ad.jp'},{'Name': 'GlobalSign Domain Verification', 'Link': 'https://support.globalsign.com/customer/portal/articles/2167245-performing-domain-verification---dns-txt-record'}]}]}]}
选择正则而非jq处理JSON的原因:
- 部分爬取得到的JSON存在格式不合法问题
- 数据中的单引号
' '会触发jq解析异常
现存问题
当前使用os模块执行bash命令时,sed命令内层包含引号导致转义冲突,现有实现代码如下:
temp = "testi.txt" os.system(f"sed -i 's/http:\/\//\nhttp:\/\//g' {temp}") os.system(f"sed -i 's/https:\/\//\nhttps:\/\//g' {temp}") os.system(f"sed -i 's/Tag/\\nTag/g' {temp}") os.system(f"sed -i '/Tag/d' {temp}") os.system(f"sed -i '/Result/d' {temp}") os.system(f"sed -i 's/\', \'*$//' {temp}") os.system(f"sed -i 's/^http:\/\///' {temp}") os.system(f"sed -i 's/^https:\/\///' {temp}") os.system(f"sed -i 's/\/.*//' {temp}")
其中3条sed命令执行出错,导致处理结果混乱,出错命令如下:
os.system(f"sed -i 's/http:\/\//\nhttp:\/\//g' {temp}") os.system(f"sed -i 's/https:\/\//\nhttps:\/\//g' {temp}") os.system(f"sed -i 's/\', \'*$//' {temp}")
已尝试使用双反斜杠转义换行符\n和内层单引号', ',但未生效。选择sed的原因是目标文件包含多行内容,认为sed比Python逐行读取文件处理更简便。
解决方案
出错核心原因有两个:
- 转义层级冲突:Python解析f-string时会优先处理字符串内的
\n转义,把它替换成实际换行符,传给shell时sed命令被拆成多段,语法直接失效。 - shell单引号转义规则限制:shell中单引号包裹的内容会被当作纯字面量,不支持任何转义字符,直接在单引号内写
'会被识别为引号闭合,后续内容变成非法shell语法。
优先推荐用subprocess传参数列表的写法,彻底绕开所有shell转义问题,不需要手动处理引号、换行的多层转义,稳定性最高:
import subprocess temp = "testi.txt" # 按顺序执行所有sed处理逻辑 subprocess.run(["sed", "-i", r"s/http:\/\//\nhttp:\/\//g", temp], check=True) subprocess.run(["sed", "-i", r"s/https:\/\//\nhttps:\/\//g", temp], check=True) subprocess.run(["sed", "-i", r"s/Tag/\nTag/g", temp], check=True) subprocess.run(["sed", "-i", "/Tag/d", temp], check=True) subprocess.run(["sed", "-i", "/Result/d", temp], check=True) subprocess.run(["sed", "-i", r"s/', '*$//", temp], check=True) subprocess.run(["sed", "-i", r"s/^http:\/\///", temp], check=True) subprocess.run(["sed", "-i", r"s/^https:\/\///", temp], check=True) subprocess.run(["sed", "-i", r"s/\/.*//", temp], check=True)
参数列表里的sed脚本用raw字符串(r""前缀)编写,Python不会解析字符串内的反斜杠转义,直接把原始的sed规则传给进程,不会出现转义错乱。
如果一定要沿用os.system拼接shell命令的写法,三条出错命令的修正版本如下,注意对应转义规则:
temp = "testi.txt" # 换行需要双反斜杠:Python转义后输出\n给shell,sed再解析为实际换行 os.system(f"sed -i 's/http:\\/\\//\\nhttp:\\/\\//g' {temp}") os.system(f"sed -i 's/https:\\/\\//\\nhttps:\\/\\//g' {temp}") # 单引号嵌套规则:临时闭合单引号,插入转义后的单引号,再重新开启单引号包裹 os.system(f"sed -i 's/'\\'', '\\''*$//' {temp}")
补充说明:如果后续遇到sed跨版本兼容问题(比如macOS自带BSD sed和Linux GNU sed语法差异),直接用Python原生逐行流式处理文本即可,性能和sed差距极小,代码可维护性更高,不需要处理多层转义问题。
内容的提问来源于stack exchange,提问作者user19219732
相关产品推荐
相关产品推荐

