我需要替换目录中多个 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
适合这个吗?
答案1
该sed
实用程序通常不适合编辑 XML 或 XHTML 文件。 XML 是一种结构化文档格式,不是面向行的。与许多标准 Unix 文本操作工具一样,该sed
实用程序是面向行的,并且不需要额外的努力就无法处理 XML 实体的编码或解码之类的事情。
您的示例文档包含节点(已更正为包含/>
在末尾)
<img src="/api/v2/epubs/urn:orm:book:381260143574/files/line.jpg" />
由于节点内的空白(空格、制表符和换行符)是任意的,并且我们不知道节点的更多属性img
或其顺序,因此使用sed
.我们还必须确保除了节点src
属性中的路径名之外,不要在其他任何地方替换路径名img
。
使用命令行 XML 解析器来执行此操作可能如下所示:
xmlstarlet ed \
-u '//img/@src[. = "/api/v2/epubs/urn:orm:book:381260143574/files/line.jpg"]' \
-v 'graphics/line.jpg' file.xhtml
我们使用xmlstarlet
,一个相当知名的命令行 XML 解析器,如果属性的原始值为 ,则用字符串替换src
每个节点的每个属性的值。img
graphics/line.jpg
/api/v2/epubs/urn:orm:book:381260143574/files/line.jpg
该命令将操作结果写入标准输出,但您可以在测试后使用其(或) 选项xmlstarlet
进行就地编辑,以确保它看起来像您期望的那样工作。--inplace
-L
如果您的img
标签看起来像<img src="...">
, 没有正确的结尾,那么您可以通过首先过滤您的 XHTML 文件来恢复此情况
xmlstarlet fo --recover --html file.xhtml
人们甚至可以设想表单上有一条管道
xmlstarlet fo --recover --html file.xhtml |
xmlstarlet ed \
-u '//img/@src[. = "/api/v2/epubs/urn:orm:book:381260143574/files/line.jpg"]' \
-v 'graphics/line.jpg'
如果您要处理的文件全部匹配模式./*.xhtml
,即,如果它们具有.xhtml
文件名后缀并且位于当前目录中,那么您将能够使用简单的 shell 循环使用上述命令中的任何一个来处理所有这些文件。
for name in ./*.xhtml; do
xmlstarlet ed --inplace \
-u '//img/@src[. = "/api/v2/epubs/urn:orm:book:381260143574/files/line.jpg"]' \
-v 'graphics/line.jpg' "$name"
done
请注意,这使用--inplace
的选项xmlstarlet
,该选项将修改文件而不进行备份。如果您在备份数据上运行此操作,那就最好了。
要对目录层次结构(即具有多个子目录的目录)中的所有 XHTML 文件运行上述命令,您可以使用find
.
find . -type f -name '*.xhtml' -exec sh -c '
for name do
xmlstarlet ed --inplace \
-u "//img/@src[. = \"/api/v2/epubs/urn:orm:book:381260143574/files/line.jpg\"]" \
-v "graphics/line.jpg" "$name"
done' sh {} +
答案2
如果它是 XHTML,您可以使用适当的 XML 编辑器对其进行编辑。这样做的优点是它不受文件布局更改的影响
首先,将示例修复为 XML(毕竟它是 XHTML 文档),
<img src="/api/v2/epubs/urn:orm:book:381260143574/files/line.jpg"/>
如果您的源文档不是真正的 XHTML,您可以通过编程方式修复它
xmlstarlet format -H file.xhtml
您可以src
使用以下命令编辑属性xmlstarlet
:
xmlstarlet edit --omit-decl --update '//img/@src' --value 'graphics/line.jpg' file.xhtml
<img src="hello"/>
或者通过组合这两个命令,
xmlstarlet fo -H file.xhtml 2>/dev/null |
xmlstarlet ed -u '//img/@src' -v 'graphics/line.jpg'
准备好后,将结果放入临时文件中,然后用修改后的版本替换原始文件。 (或者将原始文件重命名为备份,并将其用作输入以创建具有原始名称的文件。)
如果您有多个<img/>
元素,您可以为它们提供结构路径,而不仅仅是//img
.如果您只想更改那些具有特定src
属性值的属性,那也是可能的。但您的问题中确实没有足够的细节来有效地解决这些可能性。
答案3
尝试 :
sed -i.bak 's/\/api\/v2\/epubs\/urn:orm:book:381260143574\/files/graphics/g' *.xhtml
sed -i '.bak' --> sed -i.bak
'*.xhtml' --> *.xhtml
如果您不想转义斜杠,另一种选择是使用rpl
.
在基于 Debian 的发行版上:
sudo apt install rpl
rpl -b "/api/v2/epubs/urn:orm:book:381260143574/files/line.jpg" "graphics/line.jpg" *.xhtml
-b = backup
答案4
你也可以试试这个sed
。我没有包含该-i
标志,因为它在测试时不合适。
sed -E 's|(img src=").[^"]*(/.*)|\1graphics\2|' input_file
这将对我们需要保留的匹配进行分组,同时排除我们不需要保留的匹配。
(.*=.)
- 将所有内容分组,直到最后一次出现=
.[^"]*
- 是排除的匹配。[^"]
用于防止匹配到最后一个/
并匹配到下一个"
(/.*)
- 直到第二个到最后一个的所有内容/
都已被排除,同时匹配之后的剩余模式。
\1graphics\2
- 已创建两场小组赛,我们可以按任意顺序返回它们。由于graphics
需要在之后进行硬编码=
,我们可以在返回第一个分组匹配后立即插入它\1
|
- 使用管道作为分隔符,因为数据本身包含“/”斜杠,这将与seds
默认分隔符冲突。
输出
$ sed -E 's|(img src=").[^"]*(/.*)|\1graphics\2|' input_file
<img src="graphics/line.jpg"/>