在 HTML 文件中用 sed 注释掉整个超链接块
Comment out a whole hyperlink block with sed in an HTML file
我想删除某些超链接,这些超链接在许多 HTML 文件的 URL 中都包含 "legacy/"。但是,其中一些在一行中
<a href=".../legacy/..."> ... </a>\n
而其他人则不是。如何使用 sed 一次替换它们?
到目前为止我已经试过了
sed -ri 's/(.+legacy\/[[:print:]]+<\/a>.*$)/<!---->/g' wave-on-a-string.html
只替换一行中的超链接。然后我意识到 sed 一次只读一行。但是,我找不到如何匹配多行(不确定数量)的超链接块。
HTML 文件有如下内容:
<a class="other-sim-page" href="legacy/wave-on-a-string.html" dir="ltr">
<table>
<tr>
<td>
<img style="display: block;" src="../../images/icons/sim-badges/flash-badge.png" alt="Flash Logo" width="44" height="44">
</td>
<td>
<span class="other-sim-link">原始模擬教學與翻譯</span>
</td>
</tr>
</table>
</a>
...
<p>瀏覽<a href="legacy/wave-on-a-string.html#for-teachers-header">更多活動</a>。</p>
...
<a href="legacy/radiating-charge.html" class="simulation-link">
<img class="simulation-list-thumbnail" src="../../sims/radiating-charge/radiating-charge-128.png" id="simulation-display-thumbnail-radiating-charge" alt="Screenshot of the simulation 電荷輻射" width="128" height="84"/><br/>
<strong><span class="simulation-list-title">電荷輻射</span></strong><br/>
<span class="sim-display-badge sim-badge-flash"></span>
</a>
...
它只匹配并替换第二个超链接,因为它在一行中。
如果所有超链接块 (<a href="..."> ... </a>
) 跨越多行,我也想替换它们。
您没有使用正确的工具来完成这项任务。
sed
是使用正则表达式执行查找和替换的好工具,但是正则表达式(基于 DFA)无法解析嵌套结构,如 JSON 或 XML 树(如嵌套深度没有限制)。因此,我建议使用 XML/HTML 解析器。
例如你可以使用XSLT
:
输入:
$ cat webpage.html
<!DOCTYPE html>
<html>
<body>
<h1>My First Heading</h1>
<p>My first paragraph.</p>
<a href="https://www.w3schools.com">Visit W3Schools</a>
<p>My second paragraph.</p>
<a href="legacy/radiating-charge.html" class="simulation-link">
<img class="simulation-list-thumbnail" src="../../sims/radiating-charge/radiating-charge-128.png" id="simulation-display-thumbnail-radiating-charge" alt="Screenshot of the simulation 電荷輻射" width="128" height="84"/><br/>
<strong><span class="simulation-list-title">電荷輻射</span></strong><br/>
<span class="sim-display-badge sim-badge-flash"></span>
</a>
</body>
</html>
样式表:
$ cat remove_legacy.xslt
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" encoding="UTF-8" omit-xml-declaration="yes"/>
<!-- copy the whole structure recursively -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<!-- when you meet a tag a that contains href -->
<xsl:template match="//a[contains(@href,'legacy')]">
<!-- add comment starting tag -->
<xsl:text disable-output-escaping="yes">
<!--
</xsl:text>
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
<!-- add comment ending tag -->
<xsl:text disable-output-escaping="yes">
-->
</xsl:text>
</xsl:template>
</xsl:stylesheet>
输出:
$ xsltproc --html remove_legacy.xslt webpage.html
<html>
<body>
<h1>My First Heading</h1>
<p>My first paragraph.</p>
<a href="https://www.w3schools.com">Visit W3Schools</a>
<p>My second paragraph.</p>
<!--
<a href="legacy/radiating-charge.html" class="simulation-link">
<img class="simulation-list-thumbnail" src="../../sims/radiating-charge/radiating-charge-128.png" id="simulation-display-thumbnail-radiating-charge" alt="Screenshot of the simulation 電荷輻射" width="128" height="84"><br>
<strong><span class="simulation-list-title">電荷輻射</span></strong><br>
<span class="sim-display-badge sim-badge-flash"></span>
</a>
-->
</body>
</html>
可以看到不包含legacy
的href
没有注释。
使用 GNU sed for -z
并使用您在一个文件中一起提供的所有 3 个输入块作为输入:
$ sed -z '
s:@:@A:g; s:}:@B:g; s:</a>:}:g;
s:<a[^<>]* href="legacy/[^}]*}:<!--&-->:g;
s:}:</a>:g; s:@B:}:g; s:@A:@:g
' file
<!--<a class="other-sim-page" href="legacy/wave-on-a-string.html" dir="ltr">
<table>
<tr>
<td>
<img style="display: block;" src="../../images/icons/sim-badges/flash-badge.png" alt="Flash Logo" width="44" height="44">
</td>
<td>
<span class="other-sim-link">原始模擬教學與翻譯</span>
</td>
</tr>
</table>
</a>-->
...
<p>瀏覽<!--<a href="legacy/wave-on-a-string.html#for-teachers-header">更多活動</a>-->。</p>
...
<!--<a href="legacy/radiating-charge.html" class="simulation-link">
<img class="simulation-list-thumbnail" src="../../sims/radiating-charge/radiating-charge-128.png" id="simulation-display-thumbnail-radiating-charge" alt="Screenshot of the simulation 電荷輻射" width="128" height="84"/><br/>
<strong><span class="simulation-list-title">電荷輻射</span></strong><br/>
<span class="sim-display-badge sim-badge-flash"></span>
</a>-->
第一行通过将所有}s
转换为@B
然后将所有[=15=转换为}
之后无法出现在输入中的字符]s 到 }
以便 char 可以在括号表达式中取反,如要替换的字符串的正则表达式中的 [^}]
,第二行执行您想要的实际替换,第三行恢复所有 }
s 到 </a>
s,然后 @B
s 到 }
s。
操作输入以创建输入中不存在的字符是一种相当常见的 sed 习惯用法,用于解决无法在正则表达式中取反字符串的问题。请参阅 以获取另一个带有附加说明的示例。
如果您输入的字符串与您尝试匹配的字符串相似,这当然会失败,但实际上它可能足以满足您的特定输入 - 您只需要考虑它的作用并检查它的输出以验证。
试试 gnu sed
sed -E '/<a\s+.*href=.*legacy\/.*<\/a>/d; /<a\s+.*href=.*legacy\//,/<\/a>/d' wave-on-a-string.html
我想删除某些超链接,这些超链接在许多 HTML 文件的 URL 中都包含 "legacy/"。但是,其中一些在一行中
<a href=".../legacy/..."> ... </a>\n
而其他人则不是。如何使用 sed 一次替换它们?
到目前为止我已经试过了
sed -ri 's/(.+legacy\/[[:print:]]+<\/a>.*$)/<!---->/g' wave-on-a-string.html
只替换一行中的超链接。然后我意识到 sed 一次只读一行。但是,我找不到如何匹配多行(不确定数量)的超链接块。
HTML 文件有如下内容:
<a class="other-sim-page" href="legacy/wave-on-a-string.html" dir="ltr">
<table>
<tr>
<td>
<img style="display: block;" src="../../images/icons/sim-badges/flash-badge.png" alt="Flash Logo" width="44" height="44">
</td>
<td>
<span class="other-sim-link">原始模擬教學與翻譯</span>
</td>
</tr>
</table>
</a>
...
<p>瀏覽<a href="legacy/wave-on-a-string.html#for-teachers-header">更多活動</a>。</p>
...
<a href="legacy/radiating-charge.html" class="simulation-link">
<img class="simulation-list-thumbnail" src="../../sims/radiating-charge/radiating-charge-128.png" id="simulation-display-thumbnail-radiating-charge" alt="Screenshot of the simulation 電荷輻射" width="128" height="84"/><br/>
<strong><span class="simulation-list-title">電荷輻射</span></strong><br/>
<span class="sim-display-badge sim-badge-flash"></span>
</a>
...
它只匹配并替换第二个超链接,因为它在一行中。
如果所有超链接块 (<a href="..."> ... </a>
) 跨越多行,我也想替换它们。
您没有使用正确的工具来完成这项任务。
sed
是使用正则表达式执行查找和替换的好工具,但是正则表达式(基于 DFA)无法解析嵌套结构,如 JSON 或 XML 树(如嵌套深度没有限制)。因此,我建议使用 XML/HTML 解析器。
例如你可以使用XSLT
:
输入:
$ cat webpage.html
<!DOCTYPE html>
<html>
<body>
<h1>My First Heading</h1>
<p>My first paragraph.</p>
<a href="https://www.w3schools.com">Visit W3Schools</a>
<p>My second paragraph.</p>
<a href="legacy/radiating-charge.html" class="simulation-link">
<img class="simulation-list-thumbnail" src="../../sims/radiating-charge/radiating-charge-128.png" id="simulation-display-thumbnail-radiating-charge" alt="Screenshot of the simulation 電荷輻射" width="128" height="84"/><br/>
<strong><span class="simulation-list-title">電荷輻射</span></strong><br/>
<span class="sim-display-badge sim-badge-flash"></span>
</a>
</body>
</html>
样式表:
$ cat remove_legacy.xslt
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" encoding="UTF-8" omit-xml-declaration="yes"/>
<!-- copy the whole structure recursively -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<!-- when you meet a tag a that contains href -->
<xsl:template match="//a[contains(@href,'legacy')]">
<!-- add comment starting tag -->
<xsl:text disable-output-escaping="yes">
<!--
</xsl:text>
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
<!-- add comment ending tag -->
<xsl:text disable-output-escaping="yes">
-->
</xsl:text>
</xsl:template>
</xsl:stylesheet>
输出:
$ xsltproc --html remove_legacy.xslt webpage.html
<html>
<body>
<h1>My First Heading</h1>
<p>My first paragraph.</p>
<a href="https://www.w3schools.com">Visit W3Schools</a>
<p>My second paragraph.</p>
<!--
<a href="legacy/radiating-charge.html" class="simulation-link">
<img class="simulation-list-thumbnail" src="../../sims/radiating-charge/radiating-charge-128.png" id="simulation-display-thumbnail-radiating-charge" alt="Screenshot of the simulation 電荷輻射" width="128" height="84"><br>
<strong><span class="simulation-list-title">電荷輻射</span></strong><br>
<span class="sim-display-badge sim-badge-flash"></span>
</a>
-->
</body>
</html>
可以看到不包含legacy
的href
没有注释。
使用 GNU sed for -z
并使用您在一个文件中一起提供的所有 3 个输入块作为输入:
$ sed -z '
s:@:@A:g; s:}:@B:g; s:</a>:}:g;
s:<a[^<>]* href="legacy/[^}]*}:<!--&-->:g;
s:}:</a>:g; s:@B:}:g; s:@A:@:g
' file
<!--<a class="other-sim-page" href="legacy/wave-on-a-string.html" dir="ltr">
<table>
<tr>
<td>
<img style="display: block;" src="../../images/icons/sim-badges/flash-badge.png" alt="Flash Logo" width="44" height="44">
</td>
<td>
<span class="other-sim-link">原始模擬教學與翻譯</span>
</td>
</tr>
</table>
</a>-->
...
<p>瀏覽<!--<a href="legacy/wave-on-a-string.html#for-teachers-header">更多活動</a>-->。</p>
...
<!--<a href="legacy/radiating-charge.html" class="simulation-link">
<img class="simulation-list-thumbnail" src="../../sims/radiating-charge/radiating-charge-128.png" id="simulation-display-thumbnail-radiating-charge" alt="Screenshot of the simulation 電荷輻射" width="128" height="84"/><br/>
<strong><span class="simulation-list-title">電荷輻射</span></strong><br/>
<span class="sim-display-badge sim-badge-flash"></span>
</a>-->
第一行通过将所有}s
转换为@B
然后将所有[=15=转换为}
之后无法出现在输入中的字符]s 到 }
以便 char 可以在括号表达式中取反,如要替换的字符串的正则表达式中的 [^}]
,第二行执行您想要的实际替换,第三行恢复所有 }
s 到 </a>
s,然后 @B
s 到 }
s。
操作输入以创建输入中不存在的字符是一种相当常见的 sed 习惯用法,用于解决无法在正则表达式中取反字符串的问题。请参阅
如果您输入的字符串与您尝试匹配的字符串相似,这当然会失败,但实际上它可能足以满足您的特定输入 - 您只需要考虑它的作用并检查它的输出以验证。
试试 gnu sed
sed -E '/<a\s+.*href=.*legacy\/.*<\/a>/d; /<a\s+.*href=.*legacy\//,/<\/a>/d' wave-on-a-string.html