Bash Scripting: Way to switch to note if already open via CLI (Linux)?

I use a script to copy citations from Zotero to Obsidian. I’ll paste the whole script below, but the thing I’d like to modify is obsidian="obsidian://open?vault=$vault&file=$citekey.md"

The script works as intended, you select a highlighted citation in Zotero, trigger the hotkey, it opens or switches to Obsidian and then changes whichever note was active last to the note corresponding to the citation. If there was no note for this yet, it creates one with a template.

I’m hoping someone has some advice on how to modify this, so it checks if $citekey.md is already open, if so it switches to that, otherwise it creates a new tab and goes there. It’s started getting annoying when I work with several notes, and then can’t find a previous note because the script has bulldozed over it, and then I have the same note open in three different tabs.

Script and explanations

In case anyone wants to use or test this script, you do have to be aware that you need Zotero and the plugin Better BibTeX installed, plus have the “Quick Copy” option set to “Better BibTeX Citation Key Quick Copy”, otherwise the script won’t work. I have the Better BibTeX “citation key formula” set to auth + ( origyear || year ) + shorttitle(1,1) though I don’t think it matters, as long as the names in Obsidian get the same names.

#!/bin/bash

echo -n | xclip -selection clipboard
sleep 0.5
xdotool key ctrl+shift+c
sleep 0.2

# Save the citation key from the clipboard to a variable
citekey=$(xclip -o -selection clipboard)

# Check if citekey is empty
if [[ -z "$citekey" ]]; then
    echo "Error: citekey is empty. Exiting script."
    exit 1
fi

sleep 0.1

# Copies selected text or highlight in Zotero to clipboard
xdotool key ctrl+c

citation="$(xsel -o -b | sed 's/\\>/>/g' | sed -E '
    s/\([^)]* ([0-9a-z]+)\)[[:space:]]{2,}/\1/g;
    s/^> ?["“]([^"“”]+.*[^"“”]*)["”]$/\> \1/;
    s/^> ?[‘\047]([^’\047]+.*[^’\047]*)[’\047]$/\> \1/;
    s/[\\*#]//g')"

sleep 0.1

# Set your vault's directory
directory="/home/ReaderGuy42/Documents/Obsidian/PhD/03 Sources"
new_file="$directory/$citekey.md"

# Set location for Zotero's BetterBibTeX .bib file
bibfile="/home/ReaderGuy42/Documents/Zotero/Library.bib"
vault="PhD"
obsidian="obsidian://open?vault=$vault&file=$citekey.md"

# Check if the citekey exists in the .bib file before proceeding
if ! grep -q "@.*{$citekey," "$bibfile"; then
    echo "Error: citekey not found in .bib file. Exiting script."
    exit 1
fi

# Extract the author/editor information by isolating the block for this citekey
author=$(awk -v key="$citekey" '
    BEGIN {found=0}
    $0 ~ "@.*{.*" key "," {found=1}  # When the citekey starts
    found && $0 ~ /author[[:space:]]*=/ { 
        sub(/.*author[[:space:]]*=[[:space:]]*[{"]/, ""); 
        sub(/[}"].*/, "");
        print;
        found=0; 
    }
    found && $0 ~ /editor[[:space:]]*=/ {
        sub(/.*editor[[:space:]]*=[[:space:]]*[{"]/, ""); 
        sub(/[}"].*/, "");
        print;
        found=0;
    }
' "$bibfile")

title=$(grep -A1 "@.*{$citekey," "$bibfile" | grep 'title =' | sed -E 's/.*title = \{(.+)\},/\1/;s/[{}]+//g' | sed 's/\\\"a/ä/g; s/\\\"o/ö/g; s/\\\"u/ü/g; s/\\\"A/Ä/g; s/\\\"O/Ö/g; s/\\\"U/Ü/g; s/\\{\\ss\\}/ß/g; s/\\ss/ß/g')


shorttitle=$(grep -A5 "@.*{$citekey," "$bibfile" | grep 'shorttitle =' | sed -E 's/.*shorttitle = \{(.+)\},/\1/;s/[{}]+//g' | sed 's/\\\"a/ä/g; s/\\\"o/ö/g; s/\\\"u/ü/g; s/\\\"A/Ä/g; s/\\\"O/Ö/g; s/\\\"U/Ü/g; s/\\{\\ss\\}/ß/g; s/\\ss/ß/g')


year=$(awk -v key="$citekey" '
    $0 ~ "@" && $0 ~ key {found=1} 
    found && $0 ~ /year[[:space:]]*=/ {sub(/.*year[[:space:]]*=[[:space:]]*[{"]/, ""); sub(/[}"].*/, ""); print; found=0}
' "$bibfile")

item_key=$(awk -v key="$citekey" '
    $0 ~ "@" && $0 ~ key {found=1} 
    found && $0 ~ /file[[:space:]]*=/ {
        sub(/.*storage\//, "");
        sub(/\/.*/, "");
        print;
        found=0;
    }
' "$bibfile")

abstract=$(awk -v key="$citekey" '
    $0 ~ "@" && $0 ~ key {found=1}
    found && $0 ~ /abstract[[:space:]]*=/ {in_abstract=1}
    in_abstract {
        if ($0 ~ /abstract[[:space:]]*=/) {
            sub(/.*abstract[[:space:]]*=[[:space:]]*[{"]/, "", $0);
        }
        if ($0 ~ /[}"][[:space:]]*[,]*$/) {
            sub(/[}"][[:space:]]*[,]*$/, "", $0);
            in_abstract=0;
        }
        gsub(/\\\"a/, "ä", $0);
        gsub(/\\\"o/, "ö", $0);
        gsub(/\\\"u/, "ü", $0);
        gsub(/\\\"A/, "Ä", $0);
        gsub(/\\\"O/, "Ö", $0);
        gsub(/\\\"U/, "Ü", $0);
        gsub(/\{\\ss\}/, "ß", $0);
        gsub(/\\ss/, "ß", $0);
        gsub(/\{/, "", $0);
        gsub(/\}/, "", $0);

        abstract = abstract $0 " ";
    }
    !in_abstract && abstract {print abstract; abstract=""; found=0}
' "$bibfile")

# Handle cases where the information is not found
author=${author:-"Unknown"}
title=${title:-"Untitled"}
year=${year:-"Unknown"}

# Create the note if it doesn't already exist
if [ ! -f "$new_file" ]; then
    cat <<EOF > "$new_file"
---
Authors: $author
Title: "$title"
aliases: "$shorttitle"
Year: $year
Notes: unread
Category: 🖊️
DateAdded: $(date +%Y-%m-%d)
---

> [!infoblock]-
>> [!abstract]-
>> $abstract
>
>> [!kernel]-
>> \`\`\`dataviewjs
dv.executeJs(await dv.io.load("Scripts/dv/DQLkernel.md"))
>> \`\`\`
EOF
    sleep 0.3
fi

# Open the note and scroll down
xdg-open "$obsidian"
sleep 0.2
xdotool key ctrl+End
xdotool key Page_Down

# Only append citation if it's not empty
if [[ -n "$citation" ]]; then
    echo -e "\n\n###### $citation" >> "$new_file"
fi

# Delete duplicate newlines to maintain order
sed -i '/./,/^$/!d' "$new_file"

# Run the external script
python3 "/home/ReaderGuy42/Documents/Obsidian/PhD/Scripts/letterize-page-numbers.py"

sleep 0.2
xdotool key ctrl+End
sleep 0.2
xdotool key Page_Down

P.S.: The letterize-page-numbers.py is a script which gives each citation a unique identifier, so if I paste a citation from a page that already had a citation, it gives it letters, (and then also modifies the initial page number). So 5 → 5a, and subsequent citations go 5b, 5c, etc.

This way I can reference a citation via [[Smith2020What#5c]]. I can provide that script if anyone is curious.

Thanks!

There is the Mononote plugin if interested.

Thanks, that’s sort of what I was looking for, but this doesn’t work if the pane is split. The plugin only checks in the current pane and its tabs.

QuickSwitcher++ has similar functionality with 1-2 settings. Try that one, maybe.

Hm, I’m not seeing any options for this. Which settings are you talking about?

I’ll be honest with you, I rarely used the plugin and I think these were the default settings? Maybe check the docs on GH.

I know I have disabled Mononote for something that I didn’t like.

Yeah OK, that looked promising, but also doesn’t work for multiple panes. Thanks anyways :slight_smile:

Btw, if you want to upgrade you script…there’s a generous free tier with VSCode Editor’s GitHub Copilot. I use it all the time with Claude.

I’ve several suggestions:

  1. shell commands by Jarkko Linnanvirta
  2. Advanced uri by Vinzent
  3. try the zotero plugin Better Notes

Shell commands is an Obsidian community plugin which worked well for me, months ago. Currently I don’t use it anymore.
Maybe you can find even more useful community plugins under the keyword “shell”, there are some in the list.

I’ve checked Obsidians URI, since it doesn’t offer very much, there’s yet another plugin Advanced uri by Vincent.

Finally, I highly recommend you and everyone else using Zotero to install a Zotero plugin (not Obsidian plugin ) called Better notes which is awesome: with only 3 clicks you do everything.
With the first click you generate a note with all of your highlights, second click and you export this note and finally, with the third click you open this exported markdown note in Obsidian. Quick info how to use Better Notes: Zotero link to pdf annotation does not work - #2 by Jopp

But… I guess you want to insert citations selectively, instead to export your highlights and open them as separate notes in Obsidian?

I’d love to give you better solutions bc I too use / write regularly shell scripts, but so far I didn’t script zotero.

Hey, sorry for the delay and thanks for the suggestions.
I love Shell Commands and use them daily. However, since Zotero isn’t part of Obsidian, I use a separate shell script to copy text from Zotero.

I also use Advanced URI, but I haven’t figured out how to use it in this case.

Better Notes is also cool, but not really what I’m going for.

Thanks :slight_smile:

I know from experience, sometimes I rushed too much and wrote my own code instead to study software with a bit more patience. :woozy_face:

After some unsuccessful trial and errors with obsidian plugins for Zotero however, I was happy with the solution by Better Notes and didn’t research any further.

Everyone has their own workflow, I answered your question to understand if you create links between zotero pdfs and obsidian notes or if you just want to export all your zotero highlights as markdown note to Obsidian.