![Использование sed для фильтрации элементов RSS](https://rvso.com/image/1605160/%D0%98%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%20sed%20%D0%B4%D0%BB%D1%8F%20%D1%84%D0%B8%D0%BB%D1%8C%D1%82%D1%80%D0%B0%D1%86%D0%B8%D0%B8%20%D1%8D%D0%BB%D0%B5%D0%BC%D0%B5%D0%BD%D1%82%D0%BE%D0%B2%20RSS.png)
Я хотел бы написать сценарий, который:
- Принимает в качестве входных данных URL-адрес RSS-канала
- Загружает ленту
- Удаляет все
<item> ...</item>
вхождения, гдеtitle
тег не соответствует какому-либо регулярному выражению.
Следующий пример должен это проиллюстрировать. Допустим, у нас есть RSS-канал с этими тремя элементами:
- Проект Foo — Начнем!
- Что-то совсем другое
- Еще одно обновление Project Foo
Я хочу оставить только те элементы, в названии которых есть «Project Foo».
Пример входного файла:
<?xml version="1.0" encoding="iso-8859-1"?>
<rss version="2.0">
<channel>
<title>My glorious newsfeed</title>
<description>...</description>
<link>...</link>
<language>...</language>
<pubDate>...</pubDate>
<item>
<title>Project Foo - Let's get started!</title>
<link>...</link>
<description>...</description>
<pubDate>...</pubDate>
</item>
<item>
<title>Something else entirely</title>
<link>...</link>
<description>...</description>
<pubDate>...</pubDate>
</item>
<item>
<title>Another update on Project Foo</title>
<link>...</link>
<description>...</description>
<pubDate>...</pubDate>
</item>
</channel>
</rss>
Пример выходного файла:
<?xml version="1.0" encoding="iso-8859-1"?>
<rss version="2.0">
<channel>
<title>My glorious newsfeed</title>
<description>...</description>
<link>...</link>
<language>...</language>
<pubDate>...</pubDate>
<item>
<title>Project Foo - Let's get started!</title>
<link>...</link>
<description>...</description>
<pubDate>...</pubDate>
</item>
<item>
<title>Another update on Project Foo</title>
<link>...</link>
<description>...</description>
<pubDate>...</pubDate>
</item>
</channel>
</rss>
Если возможно, я бы хотел держаться подальше от подобных python
и делать это с помощью инструментов командной строки. Но я большой новичок в использовании sed
и т. д. и мне нужна помощь :)
Вот что у меня есть на данный момент:
cat sample-feed.xml \
| tr -d '\n' \
| sed $'s/\<item\>/\\\n\<item\>/g;s/\<\/channel\><\/rss\>/\\\n\<\/channel\><\/rss\>/g' \
| sed '/^\<item\>/ d'
Сначала я удаляю все новые строки. Затем я добавляю новые строки, чтобы каждая строка была <item>...</item>
на своей собственной строке. Последняя команда на данный момент удаляет все строки, которые начинаются с <item>
. Для
Результат — корректный RSS-канал без каких-либо элементов:
<?xml version="1.0" encoding="iso-8859-1"?><rss version="2.0"><channel><title>My glorious newsfeed</title><description>...</description><link>...</link><language>...</language><pubDate>...</pubDate>
</channel></rss>
Чтобы это работало с URL-адресами вместо локальных файлов, я бы просто заменил cat sample-feed.xml
на curl -s <some url>
.
Однако по-прежнему не хватает модификации команды, sed '/^\<item\>/ d'
которая удаляет только строки, начинающиеся с <item>
«Project Foo», но не содержащие его.
Итак, если бы вы могли помочь мне разобраться, что должна быть в последней строке, я был бы очень рад. С другой стороны, я уверен, что есть более элегантный способ сделать это. Из того, что я видел, sed
это довольно мощно, и это должно быть возможно сделать одной sed
командой.
С нетерпением жду ваших ответов :-)
решение1
Как и предлагалось в комментариях, я попробовал использовать xmlstarlet
для решения этой проблемы, и это работает хорошо. Вот мой скрипт
xml ed -d '//item[not(contains(title,"Project Foo"))]' < sample_rss.xml
Предположим, что содержимое фида находится в файле sample_rss.xml
. Это содержимое передается в xml ed -d
, что удаляет любую заметку, соответствующую заданному выражению XPath. Выражение XPath ищет любую, <item>
которая не имеет узла <title>
, содержащего текст "Project Foo"
.
Кажется, это работает хорошо, и я также очень доволен временем выполнения:
real 0m0.003s
user 0m0.001s
sys 0m0.002s
Остерегайтесь пространств имен
Если вы хотите, чтобы это работало с правильными каналами RSS или Atom, вы можете заметить, что feed
содержит атрибут XML Namespace ( xmlns
), как в этом примере с YouTube:
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns:yt="http://www.youtube.com/xml/schemas/2015" xmlns:media="http://search.yahoo.com/mrss/" xmlns="http://www.w3.org/2005/Atom">
...
</feed>
Тогда скрипт выше больше не будет работать! Мне пришлось изрядно помучиться, чтобы его исправить, но вот как заставить его работать:
xml ed -d '//_:entry[not(contains(_:title,"Project Foo"))]' < youtube_rss.xml
Подробнее об этой проблеме пространства имен можно узнать здесь:http://xmlstar.sourceforge.net/doc/UG/ch05.html