diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/blog/2023-06-16-regex/regex.md | 201 | ||||
| -rw-r--r-- | src/blog/2023-07-11-feed/feed.md | 340 | ||||
| -rw-r--r-- | src/blog/blog.md | 2 | ||||
| -rw-r--r-- | src/blog/feed.xml | 14 |
4 files changed, 557 insertions, 0 deletions
diff --git a/src/blog/2023-06-16-regex/regex.md b/src/blog/2023-06-16-regex/regex.md new file mode 100644 index 0000000..e6c46f1 --- /dev/null +++ b/src/blog/2023-06-16-regex/regex.md | |||
| @@ -0,0 +1,201 @@ | |||
| 1 | # UNIX text filters, part 0 of 3: regular expressions | ||
| 2 | |||
| 3 | One of the most important features of UNIX and its descendants, if | ||
| 4 | not *the* most important feature, is input / output redirection: | ||
| 5 | the output of a command can be displayed to the user, written to a | ||
| 6 | file or used as the input for another command seamlessly, without | ||
| 7 | the the program knowing which of these things is happening. This | ||
| 8 | is possible because most UNIX programs use *plain text* as their | ||
| 9 | input/output language, which is understood equally well by the three | ||
| 10 | types of users - humans, files and other running programs. | ||
| 11 | |||
| 12 | Since this is such a fundamental feature of UNIX, I thought it would | ||
| 13 | be nice to go through some of the standard tools that help the user | ||
| 14 | take advantage of it. At first I thought of doing this as part of | ||
| 15 | my *man page reading club* series, but in the end I decided to give | ||
| 16 | them their own space. My other series has also been going on for | ||
| 17 | more than a year now, so it is a good time to end it and start a | ||
| 18 | new one. | ||
| 19 | |||
| 20 | Let me then introduce you to: **UNIX text filters**. | ||
| 21 | |||
| 22 | ## Text filters | ||
| 23 | |||
| 24 | For the purpose of this blog series, a *text filter* is a program | ||
| 25 | that reads plain text from standard input and writes a modified, | ||
| 26 | or *filtered* version of the same text to standard output. According | ||
| 27 | to the introductory paragraph, this definition includes most UNIX | ||
| 28 | programs; but we are going to focus on the following three, in | ||
| 29 | increasing order of complexity: | ||
| 30 | |||
| 31 | * grep | ||
| 32 | * sed | ||
| 33 | * awk | ||
| 34 | |||
| 35 | In order to unleash the true power of these tools, we first need | ||
| 36 | to grasp the basics of | ||
| 37 | [regular expressions](https://en.wikipedia.org/wiki/Regular_expression). | ||
| 38 | And what better way to do it than following the dedicated | ||
| 39 | [OpenBSD manual page](https://man.openbsd.org/OpenBSD-7.3/re_format)? | ||
| 40 | |||
| 41 | ## (Extended) regular expressions | ||
| 42 | |||
| 43 | Regular expressions, or regexes for short, are a convenient way to | ||
| 44 | describe text patterns. They are commonly used to solve genering | ||
| 45 | string matching problems, such as determining if a given piece | ||
| 46 | of text is a valid URL. Many standard UNIX tools, including the three | ||
| 47 | we are going to cover in this series, support regexes. | ||
| 48 | |||
| 49 | Let's deal with the nasty part first: even within POSIX, there is | ||
| 50 | not one single standard for regular expressions; there are at least | ||
| 51 | two of them: Basic Regular Expressions (BREs) and Extended Regular | ||
| 52 | Expressions (ERE). As it always happens when there is more than one | ||
| 53 | standard for the same thing, other people decided to come up with | ||
| 54 | another version to replace all previous "standards", so we have also | ||
| 55 | [PCREs](https://en.wikipedia.org/wiki/Perl_Compatible_Regular_Expressions), | ||
| 56 | and probably more. [Things got out of hand quickly](https://xkcd.com/927). | ||
| 57 | |||
| 58 | In this post I am going to follow the structure of | ||
| 59 | [re_format(7)](https://man.openbsd.org/OpenBSD-7.3/re_format) and | ||
| 60 | present *extended* regular expresssions first. After that I'll point | ||
| 61 | out the differences with *basic* regular expressions. | ||
| 62 | |||
| 63 | The goal is not to provide a complete guide to regexes, but rather | ||
| 64 | an introduction to the most important features, glossing over the | ||
| 65 | nasty edge-cases. Keep also in mind that I am in no way an expert | ||
| 66 | on the subject: we are learning together, here! | ||
| 67 | |||
| 68 | ### The basics | ||
| 69 | |||
| 70 | You can think of a regular expression as a *pattern*, or a *rule*, | ||
| 71 | that describes which strings are "valid" (they are *matched* by the | ||
| 72 | regular expression) and which are not. As a trivial example, the | ||
| 73 | regular expression `hello` matches only the string "hello". A less | ||
| 74 | trivial example is the regex `.*` that matches *any* string. I'll | ||
| 75 | explain why in a second. | ||
| 76 | |||
| 77 | Beware not to confuse regular expressions with *shell globs*, i.e. | ||
| 78 | the rules for shell command expansion. Although they use similar | ||
| 79 | symbols to achieve a similar goal, they are not the same thing. See | ||
| 80 | [my post on sh(1)](../2022-09-13-sh-1) or | ||
| 81 | [glob(7)](https://man.openbsd.org/OpenBSD-7.3/glob.7) for an | ||
| 82 | explanation on shell globs. | ||
| 83 | |||
| 84 | ### General structure and terminology | ||
| 85 | |||
| 86 | A general regex looks something like this: | ||
| 87 | |||
| 88 | ``` | ||
| 89 | piece piece piece ... | piece piece piece ... | ... | ||
| 90 | ``` | ||
| 91 | |||
| 92 | A sequence of *pieces* is called a *branch*, and a regex is a | ||
| 93 | sequence of branches separated by pipes `|`. Pieces are not separated | ||
| 94 | by spaces, they are simply concatenated. | ||
| 95 | |||
| 96 | The pipes `|` are read "or": a regex matches a given string if any | ||
| 97 | of its branches does. A branch matches a given string if the latter | ||
| 98 | can be written as a sequence of strings, each matching one of the | ||
| 99 | pieces, in the given order. | ||
| 100 | |||
| 101 | Before going into what pieces are exactly, consider the following | ||
| 102 | example: | ||
| 103 | |||
| 104 | ``` | ||
| 105 | hello|world | ||
| 106 | ``` | ||
| 107 | |||
| 108 | This regex matches both the string "hello" and the string "world", | ||
| 109 | and nothing else. The pieces are the single letters composing the | ||
| 110 | two words, and as you can see they are juxtaposed without spaces. | ||
| 111 | |||
| 112 | But what else is a valid piece? In general, a piece is made up of | ||
| 113 | an *atom*, optionally followed by a *multiplier*. | ||
| 114 | |||
| 115 | ### Atoms | ||
| 116 | |||
| 117 | As we have already seen, the most simple kind of atom is a single | ||
| 118 | character. The most *general* kind of atom, on the other hand, is | ||
| 119 | a whole regular expression enclosed in parentheses `()`. Yes, regexes | ||
| 120 | are recursive. | ||
| 121 | |||
| 122 | There are some special characters: for example, a single dot `.` | ||
| 123 | matches *any* single character. The characters `^` and `$` match | ||
| 124 | an empty string at the beginning and at the end of a line, respectively. | ||
| 125 | If you want to match a special character as if it was regular, say | ||
| 126 | because you want to match strings that represent values in the | ||
| 127 | dollar currency, you can *escape* them with a backslash. For example | ||
| 128 | `\$` matches the string "$". | ||
| 129 | |||
| 130 | The last kind of atoms are *bracket expressions*, which consist of | ||
| 131 | lists of characters enclosed in brackets `[]`. A simple list of | ||
| 132 | characters in brackets, like `[xyz]`, matches any character in the | ||
| 133 | list, unless the first character is a `^`, in which case it matches | ||
| 134 | every character *not* in the list. Two characters separated by a | ||
| 135 | dash `-` denote a range: for example `[a-z]` matches every lowercase | ||
| 136 | letter and `[1-7]` matches all digits from 1 to 7. | ||
| 137 | |||
| 138 | You can also use cetain special sets of characters, like `[:lower:]` | ||
| 139 | to match every lowercase letter (same as `[a-z]`), `[:alnum:]` to | ||
| 140 | match every alphanumeric character or `[:digit:]` to match every | ||
| 141 | decimal digit. Check the | ||
| 142 | [man page](https://man.openbsd.org/OpenBSD-7.3/re_format) | ||
| 143 | for the full list. | ||
| 144 | |||
| 145 | ### Multipliers | ||
| 146 | |||
| 147 | The term "multiplier" does not appear anywhere in the manual page, I | ||
| 148 | made it up. But I think it fits, so I'll keep using it. | ||
| 149 | |||
| 150 | Multipliers allow you to match an atom repeating a specified or | ||
| 151 | unspecified amount of times. The most general one is the *bound* | ||
| 152 | multiplier, which consists of one or two comma-separated numbers | ||
| 153 | enclosed in braces `{}`. | ||
| 154 | |||
| 155 | In its most simple form, the multiplier `{n}` repeats the multiplied | ||
| 156 | atom `n` times. For example, the regex `a{7}` is equivalent to the | ||
| 157 | regex `aaaaaaa` (and it matches the string "aaaaaaa"). | ||
| 158 | |||
| 159 | The form `{n,m}` matches *any number* between `n` and `m` of copies | ||
| 160 | of the preceeding atom. For example `a{2,4}` is equivalent to | ||
| 161 | `aa|aaa|aaaa`. If the integer `m` is not specified, the multiplied | ||
| 162 | atom matches any string that consists of *at least* `n` copies of | ||
| 163 | the atom. | ||
| 164 | |||
| 165 | Now we can explain very quickly the more common multipliers `+`, | ||
| 166 | `*` and `?`: they are equivalent to `{1,}`, `{0,}` and `{0,1}` | ||
| 167 | respectively. That is to say, `+` matches at least one copy of the | ||
| 168 | atom, `*` matches any number of copies (including none) and `?` | ||
| 169 | matches either one copy or none. | ||
| 170 | |||
| 171 | ## Basic regular expressions | ||
| 172 | |||
| 173 | Basic regular expressions are less powerful than their extended | ||
| 174 | counterpart (with one exception, see below) and require more | ||
| 175 | backslashes, but it is worth knowing them, because they are used | ||
| 176 | by default in some programs (for example [ed(1)](../2022-12-24-ed)). | ||
| 177 | The main differences between EREs and BREs are: | ||
| 178 | |||
| 179 | * BREs consist of one single branch, i.e. there is no `|`. | ||
| 180 | * Multipliers `+` and `?` do not exist. | ||
| 181 | * You need to escape parentheses `\(\)` and braces `\{\}` to | ||
| 182 | use them with their special meaning. | ||
| 183 | |||
| 184 | There is one feature of BREs, called *back-reference*, that is | ||
| 185 | absent in EREs. Apparently it makes the implementation much more | ||
| 186 | complex, and it makes BREs more powerful. I noticed the author of | ||
| 187 | the manual page despises back-references, so I am not going to learn | ||
| 188 | them out of respect for them. | ||
| 189 | |||
| 190 | ## Conclusion | ||
| 191 | |||
| 192 | Regexes are a powerful tool, and they are more than worth knowing. | ||
| 193 | But, quoting from the manual page: | ||
| 194 | |||
| 195 | ``` | ||
| 196 | Having two kinds of REs is a botch. | ||
| 197 | ``` | ||
| 198 | |||
| 199 | I hope you enjoyed this post, despite the lack of practical examples. | ||
| 200 | If you want to see more applications of regular expressions, stay | ||
| 201 | tuned for the next entries on grep, sed and awk! | ||
diff --git a/src/blog/2023-07-11-feed/feed.md b/src/blog/2023-07-11-feed/feed.md new file mode 100644 index 0000000..feb5cd6 --- /dev/null +++ b/src/blog/2023-07-11-feed/feed.md | |||
| @@ -0,0 +1,340 @@ | |||
| 1 | # My minimalistic RSS feed setup | ||
| 2 | |||
| 3 | A couple of years ago I started using | ||
| 4 | [RSS](https://en.wikipedia.org/wiki/Rss) | ||
| 5 | (or [atom](https://en.wikipedia.org/wiki/Atom_(standard))) | ||
| 6 | feeds to stay up to date with websites and blogs I wanted to read. | ||
| 7 | This method is more convenient than what I used before (i.e. open | ||
| 8 | Firefox and open each website I want to follow in a new tab, one | ||
| 9 | by one), but unfortunately not every website provides an RSS feed | ||
| 10 | these days. | ||
| 11 | |||
| 12 | At first I used [newsboat](https://newsboat.org), but I soon started | ||
| 13 | disliking the curses interface - see also my rant on curses at the | ||
| 14 | end of [this other blog post](../2022-12-24-ed). Then I discovered | ||
| 15 | `sfeed`. | ||
| 16 | |||
| 17 | ## sfeed | ||
| 18 | |||
| 19 | [`sfeed`](https://codemadness.org/sfeed-simple-feed-parser.html) | ||
| 20 | is an extremely minimalistic RSS and atom reader: it reads | ||
| 21 | the xml content of feed file from standard input and it outputs one line per | ||
| 22 | feed item, with tab-separated timestamps, title, link and so on. This tool | ||
| 23 | comes bundled with other commands that can be combined with it, such as | ||
| 24 | `sfeed_plain`, which converts the output of sfeed into something | ||
| 25 | more readable: | ||
| 26 | |||
| 27 | ``` | ||
| 28 | $ curl -L https://sebastiano.tronto.net/blog/feed.xml | sfeed | sfeed_plain | ||
| 29 | 2023-06-16 02:00 UNIX text filters, part 0 of 3: regular expressions https://sebastiano.tronto.net/blog/2023-06-16-regex | ||
| 30 | 2023-05-05 02:00 I had to debug C code on a smartphone https://sebastiano.tronto.net/blog/2023-05-05-debug-smartphone | ||
| 31 | 2023-04-10 02:00 The big rewrite https://sebastiano.tronto.net/blog/2023-04-10-the-big-rewrite | ||
| 32 | 2023-03-30 02:00 The man page reading club: dc(1) https://sebastiano.tronto.net/blog/2023-03-30-dc | ||
| 33 | 2023-03-06 01:00 Resizing my website's pictures with ImageMagick and find(1) https://sebastiano.tronto.net/blog/2023-03-06-resize-pictures | ||
| 34 | ... | ||
| 35 | ``` | ||
| 36 | |||
| 37 | One can also write a configuration file with all the desired feeds | ||
| 38 | and fetch them with `sfeed_update`, or even use the `sfeed_curses` | ||
| 39 | UI. But the reasons I tried out `sfeed` in the first place is that | ||
| 40 | I *did not* want to use a curses UI, so I decided to stick with | ||
| 41 | `sfeed_plain`. | ||
| 42 | |||
| 43 | ## My wrapper script - old versions | ||
| 44 | |||
| 45 | In the project's homepage the following short script is presented to | ||
| 46 | demonstrate the flexibility of sfeed: | ||
| 47 | |||
| 48 | ``` | ||
| 49 | #!/bin/sh | ||
| 50 | url=$(sfeed_plain "$HOME/.sfeed/feeds/"* | dmenu -l 35 -i | \ | ||
| 51 | sed -n 's@^.* \([a-zA-Z]*://\)\(.*\)$@\1\2@p') | ||
| 52 | test -n "${url}" && $BROWSER "${url}" | ||
| 53 | ``` | ||
| 54 | |||
| 55 | The first line shows a list of feed items in | ||
| 56 | [dmenu](https://tools.suckless.org/dmenu) | ||
| 57 | to let the user select one, the second line opens the selected item | ||
| 58 | in a web browser. I was impressed by how simple and clever this | ||
| 59 | example was, and I decided to expand on it to build "my own" feed | ||
| 60 | reader UI. | ||
| 61 | |||
| 62 | In the first version I made, my feeds were separated in folders, | ||
| 63 | one per file, and one could select multiple feeds or even entire | ||
| 64 | folders via dmenu using | ||
| 65 | [dmenu-filepicker](https://git.tronto.net/scripts/file/dmenu-filepicker.html) | ||
| 66 | for file selection. | ||
| 67 | Once the session was terminated, all shown feeds were marked as | ||
| 68 | "read" by writing the timestamp of the last read item on a cache | ||
| 69 | file, and they were not shown again on successive calls. | ||
| 70 | |||
| 71 | This system worked fine for me, but at some point I grew tired of | ||
| 72 | feeds being marked as "read" automatically. I also disliked the | ||
| 73 | complexity of my own script. So I rewrote it from scratch, giving | ||
| 74 | up the idea of marking feeds as read. This second version can still | ||
| 75 | be found in the *old* folder of my | ||
| 76 | [scripts repo](https://git.tronto.net/scripts), but I may remove it | ||
| 77 | in the future. You will still be able to find it in the git history. | ||
| 78 | |||
| 79 | I have happily used this second version for more than a year, but | ||
| 80 | I had some minor issues with it. The main one was that, as I started | ||
| 81 | adding more and more websites to my feed list, fetching them took | ||
| 82 | longer and longer - up to 20-30 seconds; while the feed was loading, | ||
| 83 | I could not start doing other stuff, because later dmenu would have | ||
| 84 | grapped my keyboard while I was typing. Moreover, having a way to | ||
| 85 | filter out old feed items is kinda useful when you check your feed | ||
| 86 | relatively often. A few weeks ago I had enough and I decided to | ||
| 87 | rewrite my wrapper script once again. | ||
| 88 | |||
| 89 | ## My wrapper script - current version | ||
| 90 | |||
| 91 | In its current version, my `feed` scripts accepts four sub-commands: | ||
| 92 | `get` to update the feed, `menu` to prompt a dmenu selection, `clear` | ||
| 93 | to remove the old items and `show` to list all the new items. | ||
| 94 | Since `clear` is a separate action, I do not have the problem I | ||
| 95 | used to have with my first version, i.e. that feeds are automatically | ||
| 96 | marked as read even if I sometimes do not want them to be. | ||
| 97 | |||
| 98 | Let's walk through my last iteration on this script - you can find | ||
| 99 | it in my scripts repository, but I'll include it at the end of this | ||
| 100 | section too. | ||
| 101 | |||
| 102 | At first I define some variables (mostly filenames), so that I can | ||
| 103 | easily adapt the script if one day I want to move stuff around: | ||
| 104 | |||
| 105 | ``` | ||
| 106 | dir=$HOME/box/sfeed | ||
| 107 | feeddir=$dir/urls | ||
| 108 | destdir=$dir/new | ||
| 109 | olddir=$dir/old | ||
| 110 | readdir=$dir/last | ||
| 111 | menu="dmenu -l 20 -i" | ||
| 112 | urlopener=open-url | ||
| 113 | ``` | ||
| 114 | |||
| 115 | Here `open-url` is another one of my utility scripts. | ||
| 116 | |||
| 117 | To update the feed, I loop over the files in my feed folder. Each | ||
| 118 | file contains a single line with the feed's url, and the name of | ||
| 119 | the file is the name / title of the website. The results of `sfeed` | ||
| 120 | are piped into `sfeed_plain` and then saved to a file, and the most | ||
| 121 | recent time stamp for each feed is updated. | ||
| 122 | |||
| 123 | ``` | ||
| 124 | getnew() { | ||
| 125 | for f in "$feeddir"/*; do | ||
| 126 | read -r url < "$f" | ||
| 127 | name=$(basename "$f") | ||
| 128 | d="$destdir/$name" | ||
| 129 | r="$readdir/$name" | ||
| 130 | |||
| 131 | [ -f "$r" ] && read -r lr < "$r" || lr=0 | ||
| 132 | |||
| 133 | # Get new feed items | ||
| 134 | tmp=$(mktemp) | ||
| 135 | curl -s "$url" | sfeed | \ | ||
| 136 | awk -v lr="$lr" '$1 > lr {print $0}' | \ | ||
| 137 | tee "$tmp" | sfeed_plain >> "$d" | ||
| 138 | |||
| 139 | # Update last time stamp | ||
| 140 | awk -v lr="$lr" '$1 > lr {lr=$1} END {print lr}' <"$tmp" >"$r" | ||
| 141 | done | ||
| 142 | } | ||
| 143 | ``` | ||
| 144 | |||
| 145 | The next snippet is used to show the new feed items. | ||
| 146 | The `for` loop could be replaced by a simple | ||
| 147 | `cat "$destdir"/*`, but I also want to prepend each line with | ||
| 148 | the name of the website. | ||
| 149 | |||
| 150 | ``` | ||
| 151 | show() { | ||
| 152 | for f in "$destdir"/*; do | ||
| 153 | ff=$(basename "$f") | ||
| 154 | if [ -s "$f" ]; then | ||
| 155 | while read -r line; do | ||
| 156 | printf '%20s %s\n' "$ff" "$line" | ||
| 157 | done < "$f" | ||
| 158 | fi | ||
| 159 | done | ||
| 160 | } | ||
| 161 | ``` | ||
| 162 | |||
| 163 | Finally, the following one-liner can be used to prompt the user to | ||
| 164 | select and open the desired items in a browser using dmenu: | ||
| 165 | |||
| 166 | ``` | ||
| 167 | selectmenu() { | ||
| 168 | $menu | awk '{print $NF}' | xargs $urlopener | ||
| 169 | } | ||
| 170 | ``` | ||
| 171 | |||
| 172 | The "clear" action is a straightfortward file management routine, | ||
| 173 | and the rest of the script is just shell boilerplate code to parse | ||
| 174 | the command line options and sub-commands. Putting it all together, | ||
| 175 | the script looks like this: | ||
| 176 | |||
| 177 | ``` | ||
| 178 | #!/bin/sh | ||
| 179 | |||
| 180 | # RSS feed manager | ||
| 181 | |||
| 182 | # Requires: sfeed, sfeed_plain (get), dmenu, open-url (menu) | ||
| 183 | |||
| 184 | # Usage: feed [-m menu] [get|menu|clear|show] | ||
| 185 | |||
| 186 | dir=$HOME/box/sfeed | ||
| 187 | feeddir=$dir/urls | ||
| 188 | destdir=$dir/new | ||
| 189 | olddir=$dir/old | ||
| 190 | readdir=$dir/last | ||
| 191 | menu="dmenu -l 20 -i" | ||
| 192 | urlopener=open-url | ||
| 193 | |||
| 194 | usage() { | ||
| 195 | echo "Usage: feed [get|menu|clear|show]" | ||
| 196 | } | ||
| 197 | |||
| 198 | getnew() { | ||
| 199 | for f in "$feeddir"/*; do | ||
| 200 | read -r url < "$f" | ||
| 201 | name=$(basename "$f") | ||
| 202 | d="$destdir/$name" | ||
| 203 | r="$readdir/$name" | ||
| 204 | |||
| 205 | [ -f "$r" ] && read -r lr < "$r" || lr=0 | ||
| 206 | |||
| 207 | # Get new feed items | ||
| 208 | tmp=$(mktemp) | ||
| 209 | curl -s "$url" | sfeed | \ | ||
| 210 | awk -v lr="$lr" '$1 > lr {print $0}' | \ | ||
| 211 | tee "$tmp" | sfeed_plain >> "$d" | ||
| 212 | |||
| 213 | # Update last time stamp | ||
| 214 | awk -v lr="$lr" '$1 > lr {lr=$1} END {print lr}' <"$tmp" >"$r" | ||
| 215 | done | ||
| 216 | } | ||
| 217 | |||
| 218 | show() { | ||
| 219 | for f in "$destdir"/*; do | ||
| 220 | ff=$(basename "$f") | ||
| 221 | if [ -s "$f" ]; then | ||
| 222 | while read -r line; do | ||
| 223 | printf '%20s %s\n' "$ff" "$line" | ||
| 224 | done < "$f" | ||
| 225 | fi | ||
| 226 | done | ||
| 227 | } | ||
| 228 | |||
| 229 | selectmenu() { | ||
| 230 | $menu | awk '{print $NF}' | xargs $urlopener | ||
| 231 | } | ||
| 232 | |||
| 233 | while getopts "m:" opt; do | ||
| 234 | case "$opt" in | ||
| 235 | m) | ||
| 236 | menu="$OPTARG" | ||
| 237 | ;; | ||
| 238 | *) | ||
| 239 | usage | ||
| 240 | exit 1 | ||
| 241 | ;; | ||
| 242 | esac | ||
| 243 | done | ||
| 244 | |||
| 245 | shift $((OPTIND - 1)) | ||
| 246 | |||
| 247 | if [ -z "$1" ]; then | ||
| 248 | usage | ||
| 249 | exit 1 | ||
| 250 | fi | ||
| 251 | |||
| 252 | case "$1" in | ||
| 253 | get) | ||
| 254 | getnew | ||
| 255 | countnew=$(cat "$destdir"/* | wc -l) | ||
| 256 | echo "$countnew new feed items" | ||
| 257 | ;; | ||
| 258 | menu) | ||
| 259 | show | selectmenu | ||
| 260 | ;; | ||
| 261 | clear) | ||
| 262 | d="$olddir/$(date +'%Y-%m-%d-%H-%M-%S')" | ||
| 263 | mkdir "$d" | ||
| 264 | mv "$destdir"/* "$d/" | ||
| 265 | ;; | ||
| 266 | show) | ||
| 267 | show | ||
| 268 | ;; | ||
| 269 | *) | ||
| 270 | usage | ||
| 271 | exit 1 | ||
| 272 | ;; | ||
| 273 | esac | ||
| 274 | ``` | ||
| 275 | |||
| 276 | I personally like this approach of taking a simple program that | ||
| 277 | only uses standard output and standard input and wrapping it around | ||
| 278 | a shell script to have it do exactly what I want. The bulk of the | ||
| 279 | work is done the "black box" program, and the shell scripts glues | ||
| 280 | it together with the "configuration" files (in this case, my feed | ||
| 281 | folder) and presents the results to me, interactively (e.g. via | ||
| 282 | dmenu) or otherwise. | ||
| 283 | |||
| 284 | At this point my feed-comsumption workflow would be something like | ||
| 285 | this: first I `feed get`, then I do other stuff while the feed loads | ||
| 286 | and later, after a couple of minutes or so, I run a `feed show` or | ||
| 287 | `feed menu`. This is still not ideal, because whenever I want to | ||
| 288 | check my feeds I still have to wait for them to be downloaded. The | ||
| 289 | only way to go around it would be to have `feed get` run automatically | ||
| 290 | when I am not thinking about it... | ||
| 291 | |||
| 292 | ## Setting up a cron job | ||
| 293 | |||
| 294 | My personal laptop is not always connected to the internet, and in | ||
| 295 | general I do not like having too many network-related jobs running | ||
| 296 | in the background. But I do have a machine that is always connected | ||
| 297 | to the internet: the VM instance hosting this website. | ||
| 298 | |||
| 299 | Since my new setup saves my feed updates to local files, I can have | ||
| 300 | a [cron job](https://en.wikipedia.org/wiki/Cron_job) fetch the new | ||
| 301 | items and update files in a folder sync'd via | ||
| 302 | [syncthing](https://syncthing.net) (yes, I do have that *one* network | ||
| 303 | service constantly running in the background...). This setup is | ||
| 304 | similar to the one I use to [fetch my email](../2022-10-19-email-setup). | ||
| 305 | |||
| 306 | I rarely use cron, and I am always a little intimitaded by its | ||
| 307 | syntax. But in the end to have `feed get` run every hour I just | ||
| 308 | needed to add the following two lines via `crontab -e`: | ||
| 309 | |||
| 310 | ``` | ||
| 311 | MAILTO="" | ||
| 312 | 0 * * * * feed get | ||
| 313 | ``` | ||
| 314 | |||
| 315 | This is my definitive new setup, and I like it. It also has the | ||
| 316 | advantage that I only need to install `sfeed` on my server and not | ||
| 317 | locally, though I prefer to still keep it around. | ||
| 318 | |||
| 319 | So far I have found one little caveat: if my feed gets updated after | ||
| 320 | I read it and before I run a `feed clear`, some items may be deleted | ||
| 321 | before I see them. This is easilly worked around by running a quick | ||
| 322 | `feed show` before I clear the feeds up, but it is still worth | ||
| 323 | keeping in mind. | ||
| 324 | |||
| 325 | ## Conclusions | ||
| 326 | |||
| 327 | This is a summary of my last script-crafting adventure. As I was | ||
| 328 | writing this post I realized I could probably use `sfeed_update` | ||
| 329 | to simplify the script a bit, since I do not separate feeds into | ||
| 330 | folders anymore. I have also found out that `sfeed_mbox` was created | ||
| 331 | (at least I *think* it was not there the last time I checked) and I | ||
| 332 | could use it to browse my feed with a mail client - see also | ||
| 333 | [this video tutorial](https://josephchoe.com/rss-terminal) for a demo. | ||
| 334 | |||
| 335 | With all of this, did I solve my problem in the best possible way? | ||
| 336 | Definitely not. But does it work for me? Absolutely! Did I learn | ||
| 337 | something new while doing this? Kind of, but mostly I have just | ||
| 338 | excercised skills that I already had. | ||
| 339 | |||
| 340 | All in all, it was a fun exercise. | ||
diff --git a/src/blog/blog.md b/src/blog/blog.md index be9015d..6c21cd5 100644 --- a/src/blog/blog.md +++ b/src/blog/blog.md | |||
| @@ -5,6 +5,8 @@ | |||
| 5 | 5 | ||
| 6 | ## 2023 | 6 | ## 2023 |
| 7 | 7 | ||
| 8 | * 2023-07-11 [My minimalistic RSS feed setup](2023-07-11-feed) | ||
| 9 | * 2023-06-16 [UNIX text filters, part 0 of 3: regular expressions](2023-06-16-regex) | ||
| 8 | * 2023-05-05 [I had to debug C code on a smartphone](2023-05-05-debug-smartphone) | 10 | * 2023-05-05 [I had to debug C code on a smartphone](2023-05-05-debug-smartphone) |
| 9 | * 2023-04-10 [The big rewrite](2023-04-10-the-big-rewrite) | 11 | * 2023-04-10 [The big rewrite](2023-04-10-the-big-rewrite) |
| 10 | * 2023-03-30 [The man page reading club: dc(1)](2023-03-30-dc) | 12 | * 2023-03-30 [The man page reading club: dc(1)](2023-03-30-dc) |
diff --git a/src/blog/feed.xml b/src/blog/feed.xml index 58df461..9c39603 100644 --- a/src/blog/feed.xml +++ b/src/blog/feed.xml | |||
| @@ -9,6 +9,20 @@ Thoughts about software, computers and whatever I feel like sharing | |||
| 9 | </description> | 9 | </description> |
| 10 | 10 | ||
| 11 | <item> | 11 | <item> |
| 12 | <title>My minimalistic RSS feed setup</title> | ||
| 13 | <link>https://sebastiano.tronto.net/blog/2023-07-11-feed</link> | ||
| 14 | <description>My minimalistic RSS feed setup</description> | ||
| 15 | <pubDate>2023-07-11</pubDate> | ||
| 16 | </item> | ||
| 17 | |||
| 18 | <item> | ||
| 19 | <title>UNIX text filters, part 0 of 3: regular expressions</title> | ||
| 20 | <link>https://sebastiano.tronto.net/blog/2023-06-16-regex</link> | ||
| 21 | <description>UNIX text filters, part 0 of 3: regular expressions</description> | ||
| 22 | <pubDate>2023-06-16</pubDate> | ||
| 23 | </item> | ||
| 24 | |||
| 25 | <item> | ||
| 12 | <title>I had to debug C code on a smartphone</title> | 26 | <title>I had to debug C code on a smartphone</title> |
| 13 | <link>https://sebastiano.tronto.net/blog/2023-05-05-debug-smartphone</link> | 27 | <link>https://sebastiano.tronto.net/blog/2023-05-05-debug-smartphone</link> |
| 14 | <description>I had to debug C code on a smartphone</description> | 28 | <description>I had to debug C code on a smartphone</description> |
