我需要替换目录中多个 xhtml 文件中图像的路径。文件头部分如下:
<?xml version="1.0" encoding="UTF-8"?>
<html xml:lang="en-us" lang="en-us" xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops" xmlns:ns="http://www.w3.org/2001/10/synthesis">
<head>
试图用sed
命令来做,但它不起作用。可能由于特定的 sed 版本,但不确定。我有GNU sed 4.4
original path:
<img src="/api/v2/epubs/urn:orm:book:381260143574/files/line.jpg"
I need replace to:
<img src="graphics/line.jpg"
我试过了
sed -i '.bak' 's/\/api\/v2\/epubs\/urn:orm:book:381260143574\/files/graphics/g' '*.xhtml'
它返回
sed: -e expression #1, char 1: unknown command: `.'
也试过
sed -i ' ' 's/\/api\/v2\/epubs\/urn:orm:book:381260143574\/files/graphics/g' '*.xhtml'
it return
sed: can't read s/\/api\/v2\/epubs\/urn:orm:book:381260143574\/files/graphics/g: No such file or directory
sed: can't read *.xhtml: No such file or directory
sed
适合这个吗?
该
sed
实用程序通常不适合编辑 XML 或 XHTML 文件。XML 是一种结构化的文档格式,而不是面向行的。与许多标准的 Unix 文本操作工具一样,该sed
实用程序是面向行的,并且不会在没有额外工作的情况下处理 XML 实体的编码或解码之类的事情。您的示例文档包含节点(更正为
/>
最后包含)由于节点内的空格(空格、制表符和换行符)是任意的,并且我们不知道
img
节点的更多属性或其顺序,因此使用 . 解析会很麻烦sed
。我们还必须确保不要在节点src
属性之外的任何地方替换路径名。img
使用命令行 XML 解析器执行此操作可能如下所示:
我们正在使用
xmlstarlet
一个相当知名的命令行 XML 解析器,如果属性的原始值为 ,则将src
每个节点的每个属性的值替换img
为字符串。graphics/line.jpg
/api/v2/epubs/urn:orm:book:381260143574/files/line.jpg
该命令将操作结果写入标准输出,但您可以在测试后使用它的(or ) 选项
xmlstarlet
进行就地编辑,以确保它看起来像您期望的那样工作。--inplace
-L
如果您的
img
标签看起来像<img src="...">
,没有正确的结尾,那么您可以通过首先过滤您的 XHTML 文件来恢复甚至可以设想表格上的管道
如果您要处理的文件都与 pattern 匹配
./*.xhtml
,即,如果它们具有.xhtml
文件名后缀并且位于当前目录中,那么您将能够使用上述任一命令使用简单的 shell 循环来处理所有这些文件。请注意,这使用
--inplace
选项xmlstarlet
,它将修改文件而不进行备份。最好在备份数据上运行它。要在目录层次结构中的所有 XHTML 文件上运行上述内容,即在具有多个子目录的目录中,您可以使用
find
.如果它是 XHTML,您可以使用适当的 XML 编辑器对其进行编辑。这里的优点是它不受文件布局更改的影响
首先,将您的示例修改为 XML(毕竟它是一个 XHTML 文档),
如果您的源文档不是真正的 XHTML,您可以通过编程方式修复它
您可以
src
使用以下命令编辑属性xmlstarlet
:或者通过结合这两个命令,
准备好后,将结果放入临时文件,然后用修改后的版本替换原始文件。(或者将原始文件重命名为备份,并将其用作输入以创建具有原始名称的文件。)
如果您有多个
<img/>
元素,则可以为它们提供结构路径,而不仅仅是//img
. 如果您只想更改具有特定src
属性值的那些也是可能的。但是您的问题中没有足够的细节来有效地解决这些可能性。尝试 :
如果您不想转义斜杠,另一种选择是使用
rpl
.在基于 Debian 的发行版上:
rpl手册
该
-i
选项要求其值立即跟随,中间没有任何空格。所以你必须写-i.bak
. 使用空格,sed
将其解释为-i
没有值(因此文件将就地更改)并.bak
作为要运行的命令,因此是错误消息。您还需要删除引号*.html
以允许外壳扩展通配符。(您的第二次尝试基本上有同样的问题,但这里的空格作为命令不会触发错误消息。)
顺便说一句,您可以
sed
通过使用不同于的分隔符使您的命令更具可读性,/
这样您就不需要转义字符串中的斜杠来替换,例如:你也可以试试这个
sed
。我没有包含该-i
标志,因为它在测试时不合适。这将对我们需要保留的匹配进行分组,同时排除我们不需要保留的匹配。
(.*=.)
- 将所有内容分组到最后一次出现=
.[^"]*
- 是排除匹配。[^"]
用于防止匹配到最后/
并匹配到下一个"
(/.*)
- 在匹配之后的剩余模式时,直到倒数第二个的所有/
内容都已被排除。\1graphics\2
- 创建了两个小组赛,我们可以按照我们喜欢的任何顺序返回它们。由于graphics
后面需要硬编码=
,我们可以在返回第一个分组匹配后立即插入\1
|
- 管道被用作分隔符,因为数据本身包含“/”斜杠,这将与seds
默认分隔符冲突。输出
肯定有强烈的反对使用
sed
这种东西的论据,其他人也提出了这些。但是,您可能无法使用提到的专用工具。因此,如果您的输入文件的结构可以适当地预测以允许
sed
工作,那么我会这样做:通过在搜索表达式中提及一些上下文,它正在(合理地)努力确保它在正确的行上运行。
sed
用法说明:s
命令后的第一个字符指示分隔符,这使我们/
无需转义即可使用。