Making a local markdown file backup with images and metadata
09 Oct 2025
In the bearblog settings there is an ‘Export all blog data’ which provides a csv dump of all posts and metadata, which is lovely, but I wanted a markdown file version with any images also backed up locally, because it’s always the images that end up lost or broken!
So I put together a bash script to run on the csv that makes a local backup:
I haven’t automated the csv download itself, I’m happy to just do this now and then by hand.
(change OUTDIR to where you want it to go. ~/Sync is backed up with syncthing for me, as well as being in my nightly backups)
1#!/usr/bin/env bash
2set -euo pipefail
3
4CSV="$1"
5OUTDIR=~/Sync/writing/bear-blog-backup-$(date +%Y-%m-%d)
6mkdir -p "$OUTDIR"
7cd "$OUTDIR"
8
9# strip BOM, python convert to json
10json=$(python3 -c 'import csv, json, sys; print(json.dumps(list(csv.DictReader(open(sys.argv[1])))))' "$CSV")
11
12echo "$json" | jq -c '.[]' | while read -r row; do
13 slug=$(echo "$row" | jq -r '.["canonical url"] // empty')
14 [ -z "$slug" ] && slug=$(echo "$row" | jq -r '.slug')
15 file="${slug}.md"
16 [ "$file" = ".md" ] && file="unnamed-$(date +%s).md"
17 echo "Backing up $file"
18
19 {
20 while IFS= read -r key; do
21 val=$(echo "$row" | jq -c -r --arg k "$key" '.[$k]')
22 [ "$val" = "" ] && continue
23 clean_key=$(echo "$key" | tr ' ' '_' | tr '[:upper:]' '[:lower:]')
24 # flatten arrays
25 if [[ $val == \[*\] ]]; then
26 val=$(echo "$val" | jq -r '. | join(", ")')
27 fi
28 echo "${clean_key}: ${val}"
29 done < <(echo "$row" | jq -r 'keys[]' | grep -v '^content$')
30
31 echo
32 echo '---'
33 echo
34 echo "$row" | jq -r '.content'
35 } >"$file"
36
37 sed -i 's/^\xEF\xBB\xBF//' "$file"
38
39 # replace image URLs + download
40 while read -r img; do
41 url=$(echo "$img" | grep -oE 'https[^)]+')
42 fname=$(basename "${url%%\?*}")
43 sed -i "s|$url|./$fname|g" "$file"
44 curl -sL "$url" -o "$fname"
45 done < <(grep -oE '!\[[^]]*\]\(https[^)]+\)' "$file")
46done
47
48tar -czf "${OUTDIR}.tar.gz" -C "$(dirname "$OUTDIR")" "$(basename "$OUTDIR")"
49
50echo "Backup complete: $OUTDIR.tar.gz"
And here is what it captures from this recent post
1all_tags: other
2first_published_at: 2025-10-02T19:36:00+00:00
3is_page: False
4make_discoverable: True
5meta_description: "But you like her ironically, right?"
6publish: True
7published_date: 2025-10-02T19:36:00+00:00
8slug: taytay
9title: Taylor Swift and the joy of liking things
10uid: hdXSmQIbyUohKfrFfRYV
11
12---
13
14On the eve of Taylor's next album release, I am reminded how nice it is to like things. Something to look forward to! What fun. How delightful. No downsides.
15
16Growing up I thought liking things was dangerous. It made me vulnerable to attack. It was safer to be cynical, to hedge all preferences with criticisms. Give myself some breathing room in case my opinion reflected badly on me. Certainly don't let someone see my bookshelf.
17
18But a few years ago I listened to some Taylor Swift and liked it, and the extreme uncoolness of liking Taylor Swift meant there was no hedging to be done. It was either like her sincerely, or reject her fully.[^1]
19
20
21
22So I like her unapologetically. People try to give me an out sometimes, "but you like her ironically, right?" my boss once asked me. Nah. I just like her. "You like her physically, you mean?" Nah. I like her music.
23
24I found myself leaning into it a little. Listening to her more and more. It is now a thing people know about me. Max likes Taylor Swift. Odd, but wholesome. A positive thing.
25
26And I want to keep it that way. I don't want to be a fan. I don't read the discourse, I don't try to defend her on the internet. I'm a secular swifty. I won't be reading any reviews of the new album. I'm going to listen to it, and have my own opinions. Maybe listen to it again a few days later, maybe change my mind. Who knows.
27
28I will enjoy it without considering what that means about me. And that is nice.
29
30[^1]: (Also, I'm older now, and care less.)