Регулярное выражение Sed, влияющее на содержимое после регулярных выражений
У меня есть HTML-файл, содержащий следующий текст:
<!doctype html><html><head><meta charset="utf-8"><title>Test</title><base href="/"><meta name="viewport" content="width=device-width,initial-scale=1"></head><body>test</body></html>
И я запускаю это sed
команда против этого:
sed -i -e "s:<base href\s*=\s*\".*\"\s*>:<base href=\"/apps/test/\">:g" /tmp/test/index.html
Я ожидаю, что просто заменить <base href="/">
с <base href="/apps/test/">
и оставьте все в покое, но это в конечном итоге повлияет на содержимое после регулярного выражения:
<!doctype html><html><head><meta charset="utf-8"><title>Test</title><base href="/apps/test/"></head><body>test</body></html>
Это закончилось тем, что удалило всю meta
тег найден после регулярного выражения. Я просто не делаю регулярное выражение правильно?
GNU sed version 4.2.1
2 ответа
Так как *
жадный, .*
в =\s*\".*\"\s*>
соответствует крайнему правому >
имеется в наличии.
Вы можете использовать одинарные кавычки вокруг вашей команды, поэтому вам не нужно использовать \"
для двойных кавычек. Тогда вместо ".*"
, ты можешь использовать "[^"]*"
, который соответствует только следующей двойной кавычке.
Это сделало бы вашу команду в
sed 's:<base href\s*=\s*"[^"]*"\s*>:<base href="/apps/test/">:g'
Однако манипулирование HTML с помощью sed и регулярных выражений является вечно хрупким и сломается при первой же возможности. Вы можете использовать синтаксический анализатор XML/HTML, такой как xmllint, см. Ответ Романа; альтернативой являются HTML-XML-утилиты W3C с его hxpipe
а также hxunpipe
команды.
Эти команды анализируют ваш HTML и превращают его в формат, легко обрабатываемый с помощью sed, awk & friends, а затем снова превращают его в HTML:
$ hxpipe infile.html
!html ""
(html
(head
Acharset CDATA utf-8
(meta
(title
-Test
)title
Ahref CDATA /
(base
Aname CDATA viewport
Acontent CDATA width=device-width,initial-scale=1
(meta
)head
(body
-test
)body
)html
-\n
чтобы повернуть /
в href
для base
тег в /apps/test/
мы могли бы сделать это:
$ hxpipe infile.html \
| sed '/Ahref CDATA/{N;/\n(base$/s|\(CDATA\) .*|\1 /apps/test/|}' \
| hxunpipe
<!DOCTYPE html><html><head><meta charset="utf-8"><title>Test</title><meta href="/apps/test/" name="viewport" content="width=device-width,initial-scale=1"></head><body>test</body></html>
где команда sed
sed '/Ahref CDATA/{N;/\n(base$/s|\(CDATA\) .*|\1 /apps/test/|}'
или лучше читаемый
/Ahref CDATA/ { # If line matches this
N # Append next line
/\n(base$/ s|\(CDATA\) .*|\1 /apps/test/| # If in base tag, replace href
}
более или менее надежным образом вносит изменения.
Единственный правильный способ обработки данных xml/html - это использование анализаторов xml/html.
xmlstarlet
решение:
xmlstarlet fo -R -H /tmp/test/index.html | xmlstarlet ed -O -u '//base/@href' -v '/apps/test/'
Выход:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>Test</title>
<base href="/apps/test/"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
</head>
<body>test</body>
</html>
Чтобы изменить файл на месте добавить -L
опция: xmlstarlet ed -L -u ....