【问题标题】:Bulk edit complex text files批量编辑复杂的文本文件
【发布时间】:2019-06-14 19:44:48
【问题描述】:

上下文:

我继承了一个由 2.5+ MLOC 组成的遗留应用程序,分布在大约 2,000 个文件中(目前是 PHP 5.5.9、jQuery、HTML)。在这个代码库中存在各种安全漏洞、代码异味和性能问题。我的任务是安抚信息安全并将这个代码库带入 21 世纪。我正在使用 Python 来实现这些大规模编辑。

问题:

我需要编辑每个文件并修改代码。我已经用基本的字符串比较处理了单行编辑。

这是一个典型的脚本结构:

<?php

// some code here

?>

<html><head>
...
</head>
<body>
...
</body>
<script>
...
</script>
</html>

我现在需要开始修改整个 HTML 块、使用混合引号的内联 javascript(单行和双引号)以及从脚本中删除方法。

例如,我有一个 HTML 的 head 部分,如下所示:

<head>
    <meta http-equiv="X-UA-Compatible" content="IE=9" />
    <script type="text/javascript" src="jquery.form.js"></script>

    <title>TITLE</title>
    <!-- TemplateIB -->
    <link rel="Stylesheet" href="../Lib/jquery-ui-1.10.3.custom.min.css" />
    <link rel="Stylesheet" href="../Lib/3CLStyle.css" />
    <script language="JavaScript" type="text/javascript" src="../Lib/jquery-1.9.1-Combined.min.js"></script>

    <link rel="stylesheet" type="text/css" href="../Lib/jquery.datetimepicker.css" />
    <script src="../Lib/jquery.datetimepicker.full.js"></script>
</head>

我需要将其修改为:

<head>
    <meta http-equiv="X-UA-Compatible" content="IE=9" />
    <title>TITLE</title>

    <!-- Stylesheets -->
    <link rel="stylesheet" href="../Lib/jquery-ui-1.10.3.custom.min.css" />
    <link rel="stylesheet" type="text/css" href="../Lib/jquery.datetimepicker.css" />
    <link rel="stylesheet" href="../Lib/3CLStyle.css" />

    <!-- Boilerplate JS -->
    <script type="text/javascript" src="../Lib/jquery-1.9.1-Combined.min.js"></script>
    <script type="text/javascript" src="jquery.form.js"></script>
    <script type="text/javascript" src="../Lib/jquery.datetimepicker.full.js"></script>

    <!-- Custom JS -->
    <script type="text/javascript" src="../js/contactHistory.js"></script>
</head>

这里最大的障碍是,换行符并非都是统一的——这个代码库已有十多年的历史,并且已经有很多人接触过它。所以统一性是不存在的。例如,一些文件看起来像:

<head><meta http-equiv="X-UA-Compatible" content="IE=9" />
<title>
...

其他的和上面的例子一样。有些使用制表符缩进,有些使用空格。空白的应用方式有很多变化。

我看到的另一个障碍的另一个例子是这样的情况:

<input type="button" class="submit" value="Check History" onclick="CreateHistoryTable()" />

我需要修改它看起来像:

<input type="button" class="submit" value="Check History" onclick="createHistoryTable(' <?= $_GET['phone'] ? $_GET['phone'] : ''; ?>', '<?= $_GET['project'] ? $_GET['project'] : ''; ?>' )" />

可能的解决方案:

就像我说的,我已经有效地解决了单行编辑问题。对于这些更复杂的场景,我考虑过 RegEx,但这给已经很困难的情况增加了另一个层次的复杂性。

根据我的阅读,Beautiful Soup 可能会提供我需要的东西。有人可以就此提出建议吗?元素树也是另一个选项,我只是没有深入研究任何一个选项,以了解它们是否会促进我需要做的重组和深度编辑。

目前,空白的一种解决方案是美化 HTML,并使其全部统一。看起来lxml 有一个基于 Beautiful Soup 的很好的实现,它不关心空格。

下一步是块编辑——这确实是目前最大的障碍。 lxml 可能是这方面的关键,但我很难找到任何实现我想要做的示例代码。

我不是在寻找任何人来编写代码,我只是在寻找有关如何推进这些更复杂案例的意见。感谢您的帮助。

【问题讨论】:

  • 很多要在那里解析...可能太宽泛了。只是想给美丽的汤+1。惊人的图书馆。同意,进行初步清理以创造更多均匀性可能是值得的。
  • 再读一点.. 这么多行中有多少是重复的 HTML?听起来像是将事物转移到更现代的框架,利用一些布局/模板将消除很多。 干燥
  • @ficuscr 同意——现在我还在玩伤害控制。我有一个框架,但在 PHP 7.1 上开发,它不会在 5.5.9 服务器上运行。不幸的是,这个过程中还有很多其他变量,所以这个努力只是为了让我的老板和信息安全暂时远离我。
  • 听起来令人生畏。不要小看 sed 和 awk 的威力。尽早并经常提交。祝你好运!
  • 但要回答您的问题,复制了多少代码(JS、PHP、HTML)令人作呕。通过迁移到新框架,我将消除大约 70% 的重复代码。这个代码库至少可以说是一场灾难。

标签: javascript php python html


【解决方案1】:

首先,不要编写一个一次性完成所有事情的大型脚本。编写许多较小的脚本来解决一些问题。较小的将更容易编码、调试和维护。

这是使用 BeautifulSoup 进行的尝试。

from bs4 import BeautifulSoup, Comment, NavigableString
from collections import defaultdict

html = '''
<head>
    <meta http-equiv="X-UA-Compatible" content="IE=9" />
    <script type="text/javascript" src="jquery.form.js"></script>
    <script type="text/javascript" src="../js/contactHistory.js"></script>

    <title>TITLE</title>
    <!-- TemplateIB -->
    <link rel="Stylesheet" href="../Lib/jquery-ui-1.10.3.custom.min.css" />
    <link rel="Stylesheet" href="../Lib/3CLStyle.css" />
    <script language="JavaScript" type="text/javascript" src="../Lib/jquery-1.9.1-Combined.min.js"></script>

    <link rel="stylesheet" type="text/css" href="../Lib/jquery.datetimepicker.css" />
    <script src="../Lib/jquery.datetimepicker.full.js"></script>
</head>
'''

soup = BeautifulSoup(html)

# some way to distinguish the js libraries
boilerplate_js = [
    "../Lib/jquery-1.9.1-Combined.min.js",
    "jquery.form.js",
    "../Lib/jquery.datetimepicker.full.js"
 ]

tags = defaultdict(list)

# extract all the tags in the <head>...</head> element and
# put them under various keys in the tags dictionary
for tag in list(soup.html.head):
    if tag.name is not None:
        tag = tag.extract()

        # differentiate 'custom' scripts from 'boilerplate'
        if tag.name == 'script' and tag['src'] not in boilerplate_js:
            tags['custom_js'].append(tag)

        else:
            tags[tag.name].append(tag)

    # comments (and some other things) don't have tag names
    elif isinstance(tag, Comment):
        tags['Comment'].append(tag)

    # elif .... ignoring other possible tags

#create a new <head>...</head> element to be populated below        
new_head = soup.new_tag('head')

# for each kind of tag in the tags dictionary
# we append them to the new_head element
# appending '\n's and ' 's is just for formatting
#obviously, this can be refactored alot
if 'meta' in tags:
    for tag in tags.pop('meta'):
        new_head.append('\n  ')
        new_head.append(tag)

if 'title' in tags:
    new_head.append('\n  ')
    for tag in tags.pop('title'):
        new_head.append('\n  ')
        new_head.append(tag)

if 'link' in tags:
    new_head.append('\n\n  ')
    new_head.append(Comment(' Stylesheets '))
    for tag in tags.pop('link'):
        new_head.append('\n  ')
        new_head.append(tag)

if 'script' in tags:
    new_head.append('\n\n  ')
    new_head.append(Comment(' Boilerplate JS '))
    for tag in tags.pop('script'):
        new_head.append('\n  ')
        new_head.append(tag)

if 'custom_js' in tags:
    new_head.append('\n\n  ')
    new_head.append(Comment(' Custom JS '))
    for tag in tags.pop('custom_js'):
        new_head.append('\n  ')
        new_head.append(tag)

if len(tags):
    new_head.append('\n\n  ')
    new_head.append(Comment(' Other stuff '))
    for key in list(tags.keys()):
        for tag in tags.pop(key):
            new_head.append('\n  ')
            new_head.append(tag)

new_head.append('\n')

soup.html.head.replace_with(new_head)
print(soup)

输出:

<html><head>
  <meta content="IE=9" http-equiv="X-UA-Compatible"/>

  <title>TITLE</title>

  <!-- Stylesheets -->
  <link href="../Lib/jquery-ui-1.10.3.custom.min.css" rel="Stylesheet"/>
  <link href="../Lib/3CLStyle.css" rel="Stylesheet"/>
  <link href="../Lib/jquery.datetimepicker.css" rel="stylesheet" type="text/css"/>

  <!-- Boilerplate JS -->
  <script src="jquery.form.js" type="text/javascript"></script>
  <script language="JavaScript" src="../Lib/jquery-1.9.1-Combined.min.js" type="text/javascript"></script>
  <script src="../Lib/jquery.datetimepicker.full.js"></script>

  <!-- Custom JS -->
  <script src="../js/contactHistory.js" type="text/javascript"></script>

  <!-- Other stuff -->
  <!-- TemplateIB -->
</head>
</html>

【讨论】:

  • 完美!我完全打算使用不同的脚本。这是我需要能够继续前进的例子。感谢您提供如此直接而有用的答案!
猜你喜欢
  • 1970-01-01
  • 2022-11-24
  • 2020-12-17
  • 2018-04-26
  • 1970-01-01
  • 1970-01-01
  • 2013-10-09
  • 2013-01-22
  • 1970-01-01
相关资源
最近更新 更多