Booksmarks mit einem Kommando in Obsidian speichern

Schmuckbild line art von invokeAI

In die­sem Blogeintrag beschrei­be ich, wie ich einen Alfred Workflow (inkl. kos­ten­pflich­ti­gem Powerpack) erstellt habe, mit dem ich per Hotkey ein Lesezeichen auf einer dafür vor­ge­se­he­nen Obsidian Notizseite hin­zu­fü­ge. Wobei ich wie­der mit Hilfe von ChatGPT ein Shell-Script für die­sen Workflow erstellt habe. Die Vorgehensweise und auch das Script kön­nen sicher­lich auch für Implementierungen ohne die Alfred App inter­es­sant sein.

Da ich mich immer mehr mit Obsidian anfreun­de, ist es nahe­lie­gend, dass ich mei­ne Bookmarks auch in die­sem Tool samm­le, um sie wei­ter zu bear­bei­ten. Da Obsidian ein PKM-Tool (Personal Knowledge Management) ist, das lokal mit rei­nen ASCII-Dateien arbei­tet, gibt es auch außer­halb von Obsidian vie­le Möglichkeiten, mit die­sen Dateien umzu­ge­hen. Auf dem Mac als Unix-System bie­tet sich auch die Bearbeitung mit Shell-Skripten an, die auch von der Alfred App als Skript-Sprache ange­bo­ten wer­den.

Damit ChatGPT zunächst warm­lau­fen kann, habe ich zunächst ein nor­ma­les Shellskript ange­for­dert, das fol­gen­de Anforderungen erfüllt:

  1. URL und Titel wer­den als Parameter über­ge­ben — das sind die Parameter, die Alfred über­ge­ben kann
  2. Aus einem Timestamp und die­sen Daten soll das Script eine Zeil inm Markup-Format erzeu­gen
  3. Diese Zeile soll dann hin­ter einer „unsicht­ba­ren” Textzeile in eine Datei ein­ge­fügt wer­den

Punkt 3 ergibt sich dar­aus, dass mei­ne Obsidian-Dateien aus den drei Bereichen Frontmatter, Textkörper und am Ende dem Footer bestehen. Im Textbereich sol­len die Lesezeicheneinträge an einer defi­nier­ten Stelle in der Reihenfolge Neu nach Alt ein­ge­fügt wer­den. Für die Definition der Position habe ich das Markup für unsicht­ba­ren Text gewählt: %%text%%.

Diese Lösung wur­de mir dann von ChatGPT zusam­men mit den Kommentaren und einer Erklärung als ers­te Lösung prä­sen­tiert:

#!/bin/bash

# Funktion, um eine Zeile mit dem aktuellen Datum und den Parametern einzufügen
insert_line() {
  local file="$1"
  local url="$2"
  local title="$3"

  # Überprüfen, ob die Datei existiert
  if [ ! -f "$file" ]; then
    echo "Datei existiert nicht: $file"
    return 1
  fi

  # Aktuelles Datum im Format "Tag Monat Jahr"
  local date=$(date "+%d %B %Y")

  # Zeile mit Datum, Titel und URL erstellen
  local line="- $date - **$title** - [$url]($url)"

  # Zeile in die Markdown-Datei einfügen
  sed -i '' "/%%ins%%/a\\
$line
" "$file"
}

# Markdown-Datei, in die die Zeile eingefügt werden soll
file="example.md"

# URL-Parameter
url="$1"
title="$2"

# Überprüfen, ob URL und Titel übergeben wurden
if [ -z "$url" ] || [ -z "$title" ]; then
  echo "URL und/oder Titel nicht angegeben."
  echo "Verwendung: $0 <URL> <Titel>"
  exit 1
fi

# Zeile einfügen
insert_line "$file" "$url" "$title"

Zuerst wird eine Funktion mit drei inter­nen Parametern defi­niert, denen die loka­len Variablen file, url und title zuge­wie­sen wer­den, wobei die Variable file, mit dem Wert example.md wei­ter unten im Skript defi­niert wird. Es folgt eine Sicherheitsroutine, die prüft, ob die­se Datei exis­tiert. Anschließend wird in der Funktion die Variable date mit string aus dem aktu­el­len Datum und der aktu­el­len Uhrzeit gefüllt. Aus date, title und url wird dann ein String gebil­det, in dem der Ausdruck [$url]($url) einen klick­ba­ren Link erzeugt. Alternativ könn­te auch der Titel klick­bar gemacht wer­den, indem statt­des­sen [$titel]($url) geschrie­ben wird. Dieser String wird dann der Variablen line zuge­wie­sen. Diese wird dann mit dem Befehl sed hin­ter den ver­steck­ten Text %%ins%% geschrie­ben.

Vor dem Aufruf der inter­nen Funktion, wird auch noch geprüft, ob auch Übergabeparameter den ent­pre­chen­den Variablen zuge­ord­net sind Dann wird die Funktion insert_line auf­ge­ru­fen.

Da das sed-Kommando unter MacOS der BSD-Implementierung folgt, aber die meis­ten Beispielaufrufe im Netz der Gnu-Syntax ent­spre­chen, woll­te ich auf Nummer sicher gehen und frag­te nach einer awk-Implementierung.

Der awk-Befehl hat aller­dings den Nachteil, dass er eine Datei Zeile für Zeile abar­bei­tet und die Änderungen nicht direkt in die Originaldatei schrei­ben kann, daher erstellt das Skript eine tem­po­rä­re Datei mit der Ergänzung, mit der dann am Ende die Originaldatei über­schrie­ben wird.

#!/bin/bash

# Funktion, um eine Zeile mit dem aktuellen Datum und den Parametern einzufügen
insert_line() {
  local file="$1"
  local url="$2"
  local title="$3"

  # Überprüfen, ob die Datei existiert
  if [ ! -f "$file" ]; then
    echo "Datei existiert nicht: $file"
    return 1
  fi

  # Aktuelles Datum im Format "Tag Monat Jahr"
  local date=$(date "+%d %B %Y")

  # Zeile mit Datum, Titel und URL erstellen
  local line="- $date - **$title** - [$url]($url)"

  # Temporäre Datei für die Ausgabe von awk erstellen
  local tmp_file=$(mktemp)

  # Zeile in die Markdown-Datei einfügen
  awk -v line="$line" '/%%ins%%/ { print $0 RS line; next } 1' "$file" > "$tmp_file"

  # Temporäre Datei in die ursprüngliche Datei verschieben
  mv "$tmp_file" "$file"
}

# Markdown-Datei, in die die Zeile eingefügt werden soll
file="example.md"

# URL-Parameter
url="$1"
title="$2"

# Überprüfen, ob URL und Titel übergeben wurden
if [ -z "$url" ] || [ -z "$title" ]; then
  echo "URL und/oder Titel nicht angegeben."
  echo "Verwendung: $0 <URL> <Titel>"
  exit 1
fi

# Zeile einfügen
insert_line "$file" "$url" "$title"

Der awk-Aufruf ist etwas kom­ple­xer: awk -v line="$line" '/%%ins%%/ { print $0 RS line; next } 1' "$file" sucht nach der Zeichenkette %%ins%% in der Datei und fügt die Zeile line vor der ent­spre­chen­den Zeile ein. Der Rest der Datei wird mit 1 unver­än­dert aus­ge­ge­ben.

Für Alfred habe ich dann die­sen Vorschlag genom­men und an mei­ne Bedürfnisse ange­passt. Die Sicherheitsroutinen habe ich weg­ge­las­sen, da ich die Bookmark-Datei schon vor­her erstellt habe und Alfred außer­dem immer die bei­den Parameter über­gibt. Auch auf die oben erwähn­te Formatierung der URL habe ich ver­zich­tet.

url=$split1
title=$split2

a=$url
d=$title
b=`date +%d.%m.%Y`
c=" "
awk -v time=$b  -v space=$c -v link=$a '1;/%%ins%%/{print time,space, link}' /Users/ileif/Documents/Obsidian/01\ Documents/bookmarks.md  >temp.txt
rm /Users/ileif/Documents/Obsidian/01\ Documents/bookmarks.md
mv temp.txt /Users/ileif/Documents/Obsidian/01\ Documents/bookmarks.md
cat /Users/ileif/Documents/Obsidian/01\ Documents/bookmarks.md

Die Bookmarks Datei sieht bei mir wie folgt aus, wobei der nächs­te Eintrag zwi­schen dem %%ins%% und dem Bookmark vom 13.06 mit die­sem Blog ein­ge­fügt wird.

---
tags: [links]
---
Dies ist eine unkommentierte Linksammlung, die mit einem Bash-Script über Alfred mit Hilfe einer Zeile mit dem awk  Kommando erstellt wird. Achtung den versteckten Text \%\%ins\%\% nicht löschen!

%%ins%%
13.06.2023 dit und dat [https://ileif.de/](https://ileif.de/)


---
erstellt:  `=dateformat(this.file.ctime, "DDDD HH:mm")`
letzte Änderung: `=dateformat(this.file.mtime, "DDDD HH:mm")
Ordner: `=this.file.folder`

Damit sind das Skript und die Datei defi­niert, jetzt müs­sen wir uns noch den Ablauf in der Alfred App anschau­en.

Das fol­gen­den Bild zeigt den Workflow in Alfred „Programmier-Umgebung”.

Der Workflow wird mit dem Hotkey ⌘4 gestar­tet. Zuerst wird die akti­ve Applikation abge­fragt. Um die URL und den Titel des akti­ven Browserfensters zu erhal­ten, muss für Chromium- und WebKit-Browser unter­schied­lich vor­ge­gan­gen wer­den. Ich unter­schei­de hier nur zwi­schen Chrome und Safari, aber Alfred unter­stützt fast alle Browser, die auf Chrome basie­ren.

Das obi­ge Bild zeigt die Verzweigung für den jewei­li­gen Browser. Neben Chrome und Safari habe ich noch ein else mit None als Ausgang defi­niert, damit ich auch einen Ausgang habe, falls der Hotkey woan­ders gedrückt wird.

None ist mit einem Skript ver­bun­den, das einen Hinweis als Meldung aus­gibt:

query=$1

result="This works only in Safari and Chrome Browser, not in "$query

echo -n $result

Der ande­re Weg, hier am Beispiel des Chrome-Browsers, fragt den Titel und die URL des aktu­el­len Browser-Tabs ab.

Die Ausgabe der Browser Tab Box wird in der Split Argument Box wei­ter­ver­ar­bei­tet, da bei­de Browsertypen die glei­chen Parameter aus­ge­ben, wer­den sie in die­ser Box wie­der zusam­men­ge­führt.

Die Argumente wer­den an das Script in der Skriptbox über­gen und ver­ar­bei­tet.

Der Befehl cat am Ende wird als Eingabeparameter für die Benachrichtigung ver­wen­det. An die­ser Stelle könn­te auch ein echo ‘Das Lesezeichen wurde erfolgreich gespeichert’ ste­hen.

Dieser Workflow spei­chert nur eine Zeile mit Zeitstempel, Titel und URL. In einem wei­te­ren Schritt könn­te man dies etwas schö­ner gestal­ten, d.h. mit einer ent­spre­chen­den Formatierung, z.B. jeder Eintrag als Box oder ähn­li­ches.1

Das war’s. Wie immer, Verbesserungsvorschläge, Fragen oder Sonstiges bit­te in den Kommentaren hin­ter­las­sen.

  1. Anmerkung: Und wäh­rend ich die­se letz­ten Zeilen geschrie­ben habe, habe ich mich auch dar­an gesetzt und ver­sucht eine ent­spre­chen­de Lösung zu erstel­len, was sich am Ende als gar nicht so ein­fach her­aus­ge­stellt hat. Aber dazu mehr in einem ande­ren Beitrag. ↩︎

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert