Skip to content

shrimple 🇵🇱 🏳️‍⚧️

shrimple mind. shrimple problems. complex solutions. she/her

Check up on RSS/Atom dates in dozen+ lines of Bash

Posted on March[²⁰26], Thursday 12.March[²⁰26], Thursday 12. By Shrimple No Comments on Check up on RSS/Atom dates in dozen+ lines of Bash

You first and foremost need to know just whether there are any new items in your feeds. | There once were less lines of the below script when it was less robust.

set -uo pipefail
_lnp(){ echo "//*[local-name()='$1']/*[local-name()='$2']/text()" ; }
pubxpath="`_lnp item pubDate`|`_lnp entry published`;`_lnp entry updated`"
pubxpath="concat(${pubxpath//;/,\"|\",})"
alias each='while IFS= read -r; do'
alias each_none_pub='each [[ $REPLY != "|" ]] && echo "$REPLY" ||'
none_pub_filt(){ each_none_pub echo "Incorrect elements." >&2; done ; }
alias each_xmldates='xmllint --xpath "$pubxpath" - | none_pub_filt | each'
first_delim(){ REPLY=${REPLY#\|}; echo ${REPLY%%\|*} ; }
xml_pubdates(){ each_xmldates echo `first_delim`; done ; }
epoch_parse(){ date +%s -d"${REPLY:-+11111111 days}" ; }
days_ago(){ each expr $((`date +%s` - `epoch_parse`)) / 86400; done ; }
pub_days_ago(){ xml_pubdates | days_ago | sort -n | head -1 ; }
err_annot(){ awk -v p="$REPLY" '{print p ": " $0}' 1>&2; }
pub_piped(){ echo `pub_days_ago`d ago$'\t'$REPLY ; }
(each curl -sL "$REPLY" | pub_piped 2> >(err_annot) & done; wait)

Lines kept under 75 characters. Lines are self-contained and can be executed one by one, and amended interactively. Return key is not expected to be immediately pressed — you need to either add “< feeds” to put your list of feed URLs through it, or press Home/Ctrl+A and pipe it (or use Process Substitution and pipe it with <(command) syntax). Same if you save this to a file to be sourced with . or source command.

  1. Fail command if any in pipeline fails. Disallow silently accepting variables unset.
  2. _lnp function produces XPath 1.0 selector for text content of $2 elements children of $1 elements anywhere in the document and regardless of XML namespace. While RSS tags are not namespaced, Atom tags are supposed to.
  3. pubxpath is defined first as three selectors joined in different ways: //item/pubDate (RSS) and //entry/published (Atom) are bound into alternative (|), while the other is separated by a placeholder semicolon.
  4. pubxpath is immediately redefined to substitute the semicolon for a ,"|", and wrapped in concat() — results of the two selectors will be concatenated with a pipe character between them (like concat($1,"|",$2)).
  5. each is an abbreviating alias that immediately precedes a command that is supposed to be ran in a loop over the lines of piped input. It still requires a done keyword. The item from the loop is, per Bash defaults, put in the $REPLY variable.
  6. each_none_pub is an abbreviating alias that immediately precedes a command that is supposed to be ran only for lines of the piped input that are just “|” (the pipe character by itself), instead of just echoing the line.
  7. none_pub_filt is a function that is a pipeline filter to just print “Incorrect elements.” to standard error output instead of passing the line through.
  8. each_xmldates is an alias abbreviating execution of xmllint on standard input — a libxml utility that can also extract us things from XML properly using XPath — piped through the none_pub_filt and supposed to immediately precede a command plainly just like each does, but with content from xmllint.
  9. first_delim is a function that echoes to standard output the $REPLY variable without a leading pipe character and without anything that occurs after a pipe character. I used a pipe-character-delimited string as a union value representation allowing presence of both.
  10. xml_pubdates is a function that for each union kind of result from xmllint that was not filtered out as nil, applies first_delim, to its standard output.
  11. epoch_parse accepts any sort of human-readable timestamp (RFC822 from RSS or ISO8601 from Atom) and turns it into Unix epoch seconds integer using date on the $REPLY variable. In case of no produced timestamp, it fills in with one that will result in “11111111 days ago” for robustness.
  12. days_ago produces a rounded date difference in days between now and the date in $REPLY variable.
  13. pub_days_ago picks the one least count of days that resulted from comparing dates from the XML elements of the feed.
  14. err_annot is a filter that prints standard input to standard error output with the prefix of the $REPLY variable (a URL).
  15. pub_piped prints the least count of days since last publication in the format of “Nd ago” followed by a tab character and the URL.
  16. The final line lacks a redirection to it of the list of feeds as explained before this enumeration. It runs curl with flags for silence and following HTTP redirections for each line of input as the URL, printing the count of days since most recent publication alongside the URL for each. Any error messages are getting annotated with the URL before they reach the terminal. Each pipeline starting with curl is turned into a simultaneous Bash job that are then all waited for to complete. There is no job control messages from a subshell. And a subshell invocation, even with a wait after the done, can still have a redirection appended or prepended around the whole line.

Just keep a list of your feeds as a bare list of URLs, one feed URL per line. Copy-paste it from somewhere whatever machine you are on; then copy-paste the 16 portable lines into Bash and direct one into the other. No read tracking, no caches, no unreads, no aggregation, no setup — just go to the friendly website when you see there is a fresh piece over there. Produce a list of XML feeds from wherever you think of.

Idea adapted from my previous post, Atom/RSS feeds dish for a browser capable of framesets — with some Perl.

I’m the most baffled REPLY=${REPLY#\|} parameter expansion doesn’t require quoting.

Could have perhaps be a piece of neater Nu shell. But nushell is not always reliable to install and use: the issue of it segfaulting on any attempt to use its http client remains unresolved.

0 Give it a Click if you enjoyed (it does not federate)
Programming Technologies, Smol Web Habits Tags:Atom|RSS_feeds, bash, scripting, smolweb

Post navigation

Previous Post: Implementing proper natural language grep — approach
Next Post: A few links to Mozilla Sidebar panels directories

Related Posts

  • Trying to run WordPress Studio, failing Programming Technologies
  • Implementing proper natural language grep — approach Programming Technologies
  • Atom/RSS feeds dish for a browser capable of framesets — with some Perl Smol Web Habits
  • Create Block Theme with Block Editor in WordPress Playground — a first Programming Technologies
  • My setup is a distraction — netbook case Smol Web Habits
  • A few links to Mozilla Sidebar panels directories Smol Web Habits

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Atom feed for this page

Atom feed for this blog

against-messy-software Atom|RSS_feeds bash big.ugly.git.patch. chromium-and-derivatives community fragment golang kde links2 linux microsoft-edge network offpunk offpunk:lists offpunk:redirections oss-contributing perl programming-tips scripting smolweb subscribe superuser window-decorations wordpress-diving Wordpress_ActivityPub_plugin

Categories

  • Influencing Society

    (3)
  • Meta

    (3)
  • Oddities of alternate reality

    (1)
  • Programming Technologies

    (5)
  • Rookie Repairs

    (1)
  • Smol Web Habits

    (4)
  • Software Imposed On Us

    (1)
  • Wild Software Writing

    (8)
  • March 2026 (10)
  • February 2026 (5)
  • January 2026 (10)
Fediverse reactions

Post URL

Paste the post URL into the search field of your favorite open social app or platform.

Your Profile

Or, if you know your own profile, we can start things that way!
Why do I need to enter my profile?

This site is part of the ⁂ open social web, a network of interconnected social platforms (like Mastodon, Pixelfed, Friendica, and others). Unlike centralized social media, your account lives on a platform of your choice, and you can interact with people across different platforms.

By entering your profile, we can send you to your account where you can complete this action.

shrimple 🇵🇱  🏳️‍⚧️
shrimple 🇵🇱 🏳️‍⚧️
@shrimple@www.shrimple.pl
Follow

shrimple mind. shrimple problems. complex solutions. she/her

25 posts
6 followers

Follow shrimple 🇵🇱 🏳️‍⚧️

My Profile

Paste my profile into the search field of your favorite open social app or platform.

Your Profile

Or, if you know your own profile, we can start things that way!
Why do I need to enter my profile?

This site is part of the ⁂ open social web, a network of interconnected social platforms (like Mastodon, Pixelfed, Friendica, and others). Unlike centralized social media, your account lives on a platform of your choice, and you can interact with people across different platforms.

By entering your profile, we can send you to your account where you can complete this action.

  • Experimentally expanding Offpunk browser Part 1 (nightly) Wild Software Writing
  • Distributed file version management in 15 minutes of Bash Wild Software Writing
  • when DMI info like Serial not in hostnamectl output Oddities of alternate reality
  • Slash-hierarchical list names — my draft implementation for Offpunk Wild Software Writing
  • Why follow requests here and can I even be followed Meta
  • A few links to Mozilla Sidebar panels directories Smol Web Habits
  • On OVH hosting, you can’t opt out from protection Meta
  • Bugfix for list URI for my Offpunk redirections implementation draft Wild Software Writing

shrimple@shrimple.pl

Copyright © 2026 shrimple 🇵🇱 🏳️‍⚧️.

Powered by PressBook News WordPress theme