I wrote this Bash script that copies a selected, highlighted quote in Zotero (a source manager), and copies that quote into Obsidian (a markdown editor), finds the note, titled $citekey.md and pastes the quote at the bottom at the note:
#!/bin/bash
# Under Zotero Settings > Advanced > Config Editor, search for "extensions.zotero.annotations.noteTemplates.highlight" and edit template to
# <p>{{citation}} <br> > {{highlight}} {{comment}}</p>
# Copy the citation key using Ctrl+Shift+C
# must set Quick Copy settings in Zotero (under Export) to "Better BibTeX Citation Key Quick Copy"
sleep 0.2
xdotool key ctrl+shift+c
sleep 0.2
# Save the citation key from the clipboard to a variable using xclip, copies citation to clipboard, looking like cite{citekey}
TeXcitekey=$(xclip -o -selection clipboard)
# removes TeX command, leaving only citekey itself
citekey=$(echo $TeXcitekey | sed -E 's/\cite{([^}]*)}/1/')
# copies selected text or highlight in Zotero to clipboard
xdotool key ctrl+c
# Save citation as a variable
raw_citation="$(xclip -o -selection clipboard)"
# Ensure the ">" (greater than) character isn't escaped with a backslash
citation=$(echo "$raw_citation" | sed 's/\>/>/g')
# Combine the sed commands
formatted_citation=$(echo "$citation" | sed -E '
# Reduce to just the page number from (author, year, p. 5) to just 5
s/([^)]* ([0-9a-z]+))[[:space:]]{2,}/1/g;
# Remove quotation marks around the whole blockquote
s/^> ?["“]([^"“”]+.*[^"“”]*)["”]$/> 1/;
s/^> ?[‘ 47]([^’ 47]+.*[^’ 47]*)[’ 47]$/> 1/'
)
# Print the formatted citation or send it back to the clipboard
echo -n "$formatted_citation" | xclip -selection clipboard
sleep 0.1
# set your vault's directory in your system
directory="/home/Obsidian/vault"
# set location for Zotero's BetterBibTeX .bib file
bibfile="/home/Zotero/Library.bib"
# set vault
vault="?vault=vaultname&file"
# if the vault already contains a note called $citekey it switches to that file
if find "$directory" -type f | grep "$citekey"; then
new_file=$(find "$directory" -xdev -name "$citekey.md")
xdg-open "obsidian://open$vault=$citekey.md"
else
# Extract details from the .bib file
author=$(awk -v key="$citekey" '
$0 ~ "@" && $0 ~ key {found=1}
found && $0 ~ /author[[:space:]]*=/ {sub(/.*author[[:space:]]*=[[:space:]]*[{"]/, ""); sub(/[}"].*/, ""); print; found=0}
' "$bibfile")
# Extract title, handling nested braces
title=$(grep -A1 "@.*{$citekey," "$bibfile" | grep 'title =' | sed -E 's/.*title = {(.+)},/1/;s/[{}]+//g')
year=$(awk -v key="$citekey" '
$0 ~ "@" && $0 ~ key {found=1}
found && $0 ~ /year[[:space:]]*=/ {sub(/.*year[[:space:]]*=[[:space:]]*[{"]/, ""); sub(/[}"].*/, ""); print; found=0}
' "$bibfile")
# Handle cases where the information is not found
author=${author:-"Unknown"}
title=${title:-"Untitled"}
year=${year:-"Unknown"}
sleep 0.1
# If it does NOT find that note called citekey.md, it creates it
xdg-open "obsidian://new$vault=03%20Sources%2F$citekey.md"
sleep 0.3
new_file=$(find "$directory" -xdev -name "$citekey.md")
sleep 0.1
# and then inputs a literature note template with authors, title, year, and MOC (for links)
echo -e "---nAuthors: $authornTitle: "$title"nYear: $yearnMOC: n---nn" > "$new_file"
sleep 0.5
fi
sleep 0.3
# Finally! Jump to end of the note, 3 Returns (to make sure we've escaped previous blockquotes)
# Delete duplicate newlines to maintain order
#sed -i '/./,/^$/!d' "$directory/03 Sources/$citekey.md"
xdotool key ctrl+End
sleep 0.2
xdotool key Return
# xdotool key Return
xdotool key Return
# using ctrl+v is pasting because it plops the text down all at once instead of one letter at a time
xdotool key ctrl+v
#sleep 1
# Delete duplicate newlines to maintain order - working
sed -i '/./,/^$/!d' "$directory/03 Sources/$citekey.md"
This was working reasonably well, and I don’t know if something changed with an update in the last few days or if I’m just using it more so I notice errors.
The intended use is that I trigger a hotkey for this script when I’ve selected a quote in Zotero, and it takes the citekey from the .bib file, looks for a markdown file with that name and pastes it there.
However, this is the problem I’ve been having. With longer quotes (I haven’t determined if there’s a maximum length) it can’t seem to deal anymore and it then tries to find a file with the name of the entire quote instead of just the citekey, which of course doesn’t exist, and I get the error in Obsidian ENAMETOOLONG
followed by the quote.
I can’t narrow down the problem because sometimes it works, even with long quotes. It’s not consistent on when it finds the citekey and when it doesn’t. I don’t know if there’s a problem with xclip or my use of it.
Example of what is supposed to happen, from Ted Chiang’s 2005 What’s expected of us:
This is what is copied from Zotero:
(Chiang, 2005, p. 150)
> “By now you’ve probably seen a Predictor; millions of them have been sold by the time you’re reading this.”
And this is what Obsidian gets:
150
> “By now you’ve probably seen a Predictor; millions of them have been sold by the time you’re reading this.”
What happens in the background is that it also hits Ctrl+Shift+C which copies the citekey (here Chiang2005What
), which is then used to find the note (here Chiang2005What.md
) or if that doesn’t exist create it.
I’ve also just noticed that this
TeXcitekey=$(xclip -o -selection clipboard)
# removes TeX command, leaving only citekey itself
citekey=$(echo $TeXcitekey | sed -E 's/\cite{([^}]*)}/1/')
should be redundant because somehow Ctrl+Shift+C is copying just the citekey, not as assumed here cite{citkey}
however, when I try simplifying this by just having
citekey=$(xclip -o -selection clipboard)
and removing the rest it no longer works, which adds to my confusion.
Any advice on how to improve this or make it more consistent is appreciated!!
Thanks