Skip to content

Instantly share code, notes, and snippets.

@norm
Last active June 11, 2025 13:22
Show Gist options
  • Save norm/7a20c85cb0aeddff3c517ead479d985c to your computer and use it in GitHub Desktop.
Save norm/7a20c85cb0aeddff3c517ead479d985c to your computer and use it in GitHub Desktop.
5e magic item finder

Magic Items lookup

Lookup 5e-style magic items in an Obsidian vault or a directory. Assumes one file per item, and formatting similar to:

# Noun of Action

* **Rarity:** uncommon
* **Type:** Wondrous item
* **Attunement:** No
* **Source:** 5.1 SRD (D&D 2014)

It does magic stuff...

but should also work with explicit properties:

---
rarity: uncommon
type: wondrous-item
attunement: [bard]
source: the-internet
---
# Lyre of Enchantent

Explanation of magic item...

Only tested on macOS, I'm afraid.

Usage

magic-items -h
magic-items [-a attune] [-b base_dir] [-d items_dir] [-f force_dir]
            [-n num] [-r rarity] [-s source] [-t type] [-v vault]
            [match]

Options:

-h  show help

-a *str*
    search for items with attunement containing *str*

-b *base_dir*
    set the location of your vaults to *base_dir*

-d *dir*
    search within *dir* in the vault

-f *dir*
    force the directory to *dir* (disables -o, ignores any
    values in the environment or added via -b, -d, -v)

-n *number*
    show at most *number* results, picked randomly

-o  open matches in Obsidian

-r *str*
    search for items with rarity of *str*

-s *str*
    search for items with a source containing *str*

-t *type*
    restrict results to the given *type* (eg potion, scroll, weapon)

-v *name*
    set the vault to *name*

*match*
    restrict results to files that contain word/phrase *match*
    (case-insensitive)

Environment Variables:

  • OBSIDIAN_BASE_DIR

Set to the directory that contains your vaults. Can be overridden with the -b argument.

  • OBSIDIAN_DND_VAULT

The name of the vault that contains the magic items. Can be overridden with the -v argument.

Example

% magic-items -f /Users/norm/Downloads/5e_artisanal_database/magic_items/md/5.1_srd_\(d\&d_2014\) dragon
necklace-of-adaptation.md
dragon-slayer.md
orb-of-dragonkind.md
deck-of-illusions.md
dragon-scale-mail.md
arrow-of-slaying.md
#!/usr/bin/env -S bash -euo pipefail
base_dir="${OBSIDIAN_BASE_DIR:-$HOME/Library/Mobile Documents/iCloud~md~obsidian/Documents}"
vault="${OBSIDIAN_DND_VAULT:-dnd}"
items_dir='magic-items'
working_dir=''
type=''
num_match=0
rarity=''
source=''
match=''
attune=''
open=0
function _pushd { pushd "$1" >/dev/null; }
function _popd { popd >/dev/null; }
function main {
local -a matches
local content
local filename
_pushd "$working_dir"
# initial filtering to find matches where specified
while read filename; do
content=$(sed -e 's/\*\*//g' "$filename")
if [ -n "$match" ]; then
if ! egrep -qi "$match" $filename; then
continue
fi
fi
if [ -n "$rarity" ]; then
if ! egrep -qi "rarity: *$rarity" <(echo "$content"); then
continue
fi
fi
if [ -n "$type" ]; then
if ! egrep -qi "type:.*$type" <(echo "$content"); then
continue
fi
fi
if [ -n "$source" ]; then
if ! egrep -qi "source:.*$source" <(echo "$content"); then
continue
fi
fi
if [ -n "$attune" ]; then
if ! egrep -qi "attunement:.*$attune" <(echo "$content"); then
continue
fi
fi
matches+=($filename)
done < <(find . -name '*.md' | cut -c3-)
# post-filtering of matching files subset
local filter='cat'
if [ "$num_match" -gt 0 ]; then
filter="shuf -n $num_match"
fi
# declare the matches
while read filename; do
if [ "$open" = 1 ]; then
open "obsidian://vault/${vault}/${items_dir}/${filename}"
else
echo "$filename"
fi
done < <(printf "%s\n" "${matches[@]}" | $filter)
_popd
}
# FIXME check with docs
while getopts "ha:d:f:n:or:s:t:v:" option; do
case $option in
h) exec perldoc -T "$0" ;;
a) attune="$OPTARG" ;;
b) base_dir="$OPTARG" ;;
d) items_dir="$OPTARG" ;;
f) working_dir="$OPTARG" ;;
n) num_match="$OPTARG" ;;
o) open=1 ;;
r) rarity="$OPTARG" ;;
s) source="$OPTARG" ;;
t) type="$OPTARG" ;;
v) vault="$OPTARG" ;;
esac
done
shift $(( OPTIND -1 ))
match="${1:-}"
if [ -n "$working_dir" ]; then
# if not in a vault, using Obsidian URL to open won't work
open=0
else
working_dir="${base_dir}/${vault}/${items_dir}"
fi
if [ ! -d "$working_dir" ]; then
echo "No such directory: ${working_dir}"
exit 1
fi
main "$@"
exit
=pod
=head1 Usage:
magic-items
[-a attune]
[-b base_dir]
[-d items_dir]
[-f force_dir]
[-n num]
[-r rarity]
[-s source]
[-t type]
[-v vault]
[match]
=head1 Options:
=over
=item B<-h>
show help
=item B<-a> I<str>
search for items with attunement containing I<str>
=item B<-b> I<base_dir>
set the location of your vaults to I<base_dir>
=item B<-d> I<dir>
search within I<dir> in the vault
=item B<-f> I<dir>
force the directory to I<dir> (disables B<-o>, ignores any
values in the environment or added via B<-b>, B<-d>, B<-v>)
=item B<-n> I<number>
show at most I<number> results, picked randomly
=item B<-o>
open matches in Obsidian
=item B<-r> I<str>
search for items with rarity of I<str>
=item B<-s> I<str>
search for items with a source containing I<str>
=item B<-t> I<type>
restrict results to the given I<type> (eg potion, scroll, weapon)
=item B<-v> I<name>
set the vault to I<name>
=item I<match>
restrict results to files that contain word/phrase I<match> (case-insensitive)
=back
=head1 Environment Variables:
=over
=item OBSIDIAN_BASE_DIR
Set to the directory that contains your vaults. Can be overridden with
the B<-b> I<dir> argument.
=item OBSIDIAN_DND_VAULT
The name of the vault that contains the magic items. Can be overridden with
the B<-v> I<vault> argument.
=back
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment