In diesem Blogeintrag beschreibe ich, wie ich einen Alfred Workflow (inkl. kostenpflichtigem Powerpack) erstellt habe, mit dem ich per Hotkey ein Lesezeichen auf einer dafür vorgesehenen Obsidian Notizseite hinzufüge. Wobei ich wieder mit Hilfe von ChatGPT ein Shell-Script für diesen Workflow erstellt habe. Die Vorgehensweise und auch das Script können sicherlich auch für Implementierungen ohne die Alfred App interessant sein.
Da ich mich immer mehr mit Obsidian anfreunde, ist es naheliegend, dass ich meine Bookmarks auch in diesem Tool sammle, um sie weiter zu bearbeiten. Da Obsidian ein PKM-Tool (Personal Knowledge Management) ist, das lokal mit reinen ASCII-Dateien arbeitet, gibt es auch außerhalb von Obsidian viele Möglichkeiten, mit diesen Dateien umzugehen. Auf dem Mac als Unix-System bietet sich auch die Bearbeitung mit Shell-Skripten an, die auch von der Alfred App als Skript-Sprache angeboten werden.
Damit ChatGPT zunächst warmlaufen kann, habe ich zunächst ein normales Shellskript angefordert, das folgende Anforderungen erfüllt:
- URL und Titel werden als Parameter übergeben — das sind die Parameter, die Alfred übergeben kann
- Aus einem Timestamp und diesen Daten soll das Script eine Zeil inm Markup-Format erzeugen
- Diese Zeile soll dann hinter einer „unsichtbaren” Textzeile in eine Datei eingefügt werden
Punkt 3 ergibt sich daraus, dass meine Obsidian-Dateien aus den drei Bereichen Frontmatter, Textkörper und am Ende dem Footer bestehen. Im Textbereich sollen die Lesezeicheneinträge an einer definierten Stelle in der Reihenfolge Neu nach Alt eingefügt werden. Für die Definition der Position habe ich das Markup für unsichtbaren Text gewählt: %%text%%
.
Diese Lösung wurde mir dann von ChatGPT zusammen mit den Kommentaren und einer Erklärung als erste Lösung präsentiert:
#!/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 internen Parametern definiert, denen die lokalen Variablen file, url und title zugewiesen werden, wobei die Variable file, mit dem Wert example.md weiter unten im Skript definiert wird. Es folgt eine Sicherheitsroutine, die prüft, ob diese Datei existiert. Anschließend wird in der Funktion die Variable date mit string aus dem aktuellen Datum und der aktuellen Uhrzeit gefüllt. Aus date, title und url wird dann ein String gebildet, in dem der Ausdruck [$url]($url) einen klickbaren Link erzeugt. Alternativ könnte auch der Titel klickbar gemacht werden, indem stattdessen [$titel]($url) geschrieben wird. Dieser String wird dann der Variablen line zugewiesen. Diese wird dann mit dem Befehl sed hinter den versteckten Text %%ins%%
geschrieben.
Vor dem Aufruf der internen Funktion, wird auch noch geprüft, ob auch Übergabeparameter den entprechenden Variablen zugeordnet sind Dann wird die Funktion insert_line aufgerufen.
Da das sed-Kommando unter MacOS der BSD-Implementierung folgt, aber die meisten Beispielaufrufe im Netz der Gnu-Syntax entsprechen, wollte ich auf Nummer sicher gehen und fragte nach einer awk-Implementierung.
Der awk-Befehl hat allerdings den Nachteil, dass er eine Datei Zeile für Zeile abarbeitet und die Änderungen nicht direkt in die Originaldatei schreiben kann, daher erstellt das Skript eine temporäre Datei mit der Ergänzung, mit der dann am Ende die Originaldatei überschrieben 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 komplexer: 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 entsprechenden Zeile ein. Der Rest der Datei wird mit 1
unverändert ausgegeben.
Für Alfred habe ich dann diesen Vorschlag genommen und an meine Bedürfnisse angepasst. Die Sicherheitsroutinen habe ich weggelassen, da ich die Bookmark-Datei schon vorher erstellt habe und Alfred außerdem immer die beiden Parameter übergibt. Auch auf die oben erwähnte Formatierung der URL habe ich verzichtet.
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ächste Eintrag zwischen dem %%ins%%
und dem Bookmark vom 13.06 mit diesem Blog eingefü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 definiert, jetzt müssen wir uns noch den Ablauf in der Alfred App anschauen.
Das folgenden Bild zeigt den Workflow in Alfred „Programmier-Umgebung”.

Der Workflow wird mit dem Hotkey ⌘4 gestartet. Zuerst wird die aktive Applikation abgefragt. Um die URL und den Titel des aktiven Browserfensters zu erhalten, muss für Chromium- und WebKit-Browser unterschiedlich vorgegangen werden. Ich unterscheide hier nur zwischen Chrome und Safari, aber Alfred unterstützt fast alle Browser, die auf Chrome basieren.

Das obige Bild zeigt die Verzweigung für den jeweiligen Browser. Neben Chrome und Safari habe ich noch ein else mit None als Ausgang definiert, damit ich auch einen Ausgang habe, falls der Hotkey woanders gedrückt wird.
None ist mit einem Skript verbunden, das einen Hinweis als Meldung ausgibt:
query=$1
result="This works only in Safari and Chrome Browser, not in "$query
echo -n $result
Der andere Weg, hier am Beispiel des Chrome-Browsers, fragt den Titel und die URL des aktuellen Browser-Tabs ab.

Die Ausgabe der Browser Tab Box wird in der Split Argument Box weiterverarbeitet, da beide Browsertypen die gleichen Parameter ausgeben, werden sie in dieser Box wieder zusammengeführt.

Die Argumente werden an das Script in der Skriptbox übergen und verarbeitet.

Der Befehl cat am Ende wird als Eingabeparameter für die Benachrichtigung verwendet. An dieser Stelle könnte auch ein echo ‘Das Lesezeichen wurde erfolgreich gespeichert
’ stehen.
Dieser Workflow speichert nur eine Zeile mit Zeitstempel, Titel und URL. In einem weiteren Schritt könnte man dies etwas schöner gestalten, d.h. mit einer entsprechenden Formatierung, z.B. jeder Eintrag als Box oder ähnliches.1
Das war’s. Wie immer, Verbesserungsvorschläge, Fragen oder Sonstiges bitte in den Kommentaren hinterlassen.
- Anmerkung: Und während ich diese letzten Zeilen geschrieben habe, habe ich mich auch daran gesetzt und versucht eine entsprechende Lösung zu erstellen, was sich am Ende als gar nicht so einfach herausgestellt hat. Aber dazu mehr in einem anderen Beitrag. ↩︎
Schreibe einen Kommentar