Dear Devonthinkers

I’d like to know if there any script (or a easy scriptable solution) to this scenario:

  • execute an action capable to extract from a selected text file (.md, txt, or other) any word with the syntax @tagname and add it as DT/Finder tag to that file.

With this solution, if we produce a text file - in a indexed folder - outside DT (for e.g. Editorial on iPad) and write the tags that we want to add to that note with the syntax @tagname, then, on DT, with the script, these words would be added as tags (and removed from the text file).

Any suggestion?


Yes, this would be possible. However, you have to determine if you have any multiWord Tags, or if the Tags are segregated in a controlled way.

I’d suggest making the scripting easier by structuring your input using a standard HTML construct when you write your documents in Editorial:

<meta name="keywords" content="blueberry pie,apples,peach compote">

A benefit of this is that most Markdown interpreters – including Editorial’s and DEVONthink’s – will ignore this when the text is rendered, you’ll only see it when viewing the markdown source. I suggest this because when you use that HTML your script can more easily find the tags in the source file and convert them to DEVONthink. This will make scripting easier, and avoids the multiword problem that Jim mentions. You’ll probably also be able to find some sample scripts at MacScripter.net and in this forum (and elsewhere on the internet) that parse text containing that meta statement or something similar in HTML. I’d probably use REGEX to locate the string if I was doing this.

Once again, thanks for your reply.

About your warnings or future problems, I have to mention this: I’m starting my workflow. Hence, I can define my tagging strategy according to the limitations of the script:
a) if I need multiword tags I can define something like @word1:word2, @word1-word2, etc;
b) I can separate tags in the way defined by the script.

My main problem is the fact that I’m a newbie in devonthink and macworld… Therefore I’m on the basic level: I already understand the usefulness of scripts but the language and the tools used are yet a strange world.

To overcome the multiple-word problem, I suggest that rather than @word1:word2 you do something like @this is a tag@ – using a beginning and end delimiter to surround your tags.

Someone might jump in here and write the script for you, which is fine, but if you want to explore the skills needed for your script I suggest you could look into:

How to use “AppleScript’s Text Item Delimiters” – this feature of AppleScript is helpful for parsing text for specific delimiters (such as I suggested above) and extracting the text inside.

How to write tags to DEVONthink records – look at the “Convert Keywords to Tags” script I mentioned above, and you’ll see how to do this.

Korm, once again thanks.

First of all I need to reinforce my newbie condition on OS X, DT… and some of your technical language is yet strange to me. I’m very grateful but I need to warning you about this. And if my answer does not make any sense… I apologize for that.

About your main suggestion - create my documents in HTML format - I have to say that is a limitation to my workflow. If I create a note in Editorial I want the ability to edit that document on Ulysses, Devonthink and Editorial (a shared folder on Dropbox). That is, if necessary I want to edit files between this apps (and add or remove tags).
I may be wrong, but I think that the HTML format can’t be edit on Ulysses or DT! Right? (not to mention my inability to write on HTML language…)

Korm, I wrote my last post before read your last one.
Thank you for your help. I’ll follow your suggestions.

Editorial is made for Markdown.
Ulysses is made for Markdown.
DEVONthink supports creating and editing Markdown documents.

korm’s suggestion of HTML meta / keywords is applicable in this case.

PS: Do consider that iOS doesn’t natively support Tags. An iOS application has to write their own support for any Tags.

Here’s a pretty stupid script that will do what the OP was looking for.

Usage: use different delimiters for the beginning and end of the tags. In this case, I’m using @ and % for begin and end, respectively. The script needs enhanced if you want to use the same delimiter for begin and end or want to use my suggestion. Put one delimited tag string per line into the file (anywhere in the file – just remember one tag string per line.) Again, this could be modified to put the tags on one line. Finally, this script runs on a single document at a time, and could be modded to run on a selection. Not too difficult to do these mods, but I have no interest in further development until I find a reason to want this for my own use. The script can be optimized to condense syntax, but I chose to make the logic more explicit for pedagogic purposes.

With a hat tip and thanks to Brati’s Loverfor the perpetually useful set of library’s that have kept on ticking for years.

(* Find delimited tags in a text file and add them to the existing tags of that file.

(c) 20160613 korm

with credit to the Brati's Lover AppleScript Library for the "stringBetween" and "explode" functions

Use: select a document that has tags 
written to it, delimited with the "beginTag" and "endTag" indicators
that can be set as properties in the script.  There should be one tag
per line.

Someone else can modify this to improve on the delimiters and allow
multiple tags per line.

property beginTag : "@"
property endTag : "%"

tell application id "DNtp"
		set theRecord to content record
		set theText to the plain text of theRecord
		set theParagraphs to paragraphs of theText as list
		set theTags to the tags of theRecord
		repeat with thisParagraph in theParagraphs
			set paraTest to thisParagraph as string
			set theStr to my stringBetween(paraTest, beginTag, endTag)
			if theStr ≠ "" then
				set theTags to theTags & theStr
			end if
		end repeat
		set the tags of theRecord to theTags
	end try
end tell

on stringBetween(str, head, tail)
	local str, head, tail
		if str does not contain head then return ""
		if str does not contain tail then return ""
		set str to item 2 of my explode(head, str)
		set str to item 1 of my explode(tail, str)
		return str
	on error eMsg number eNum
		error "Can't stringBetween: " & eMsg number eNum
	end try
end stringBetween

on explode(delimiter, input)
	local delimiter, input, ASTID
	set ASTID to AppleScript's text item delimiters
		set AppleScript's text item delimiters to delimiter
		set input to text items of input
		set AppleScript's text item delimiters to ASTID
		return input --> list
	on error eMsg number eNum
		set AppleScript's text item delimiters to ASTID
		error "Can't explode: " & eMsg number eNum
	end try
end explode

Dear Korm,

Many, many thanks.
I’ll try the script (“pretty stupid script” - a “very intelligent script” for me) and learn with your “pedagogical” explanations.
The first thing to do in Editorial will be a creation of one snippet “@tag%” to improve the tags creation.

Once again, thanks for your time and patience.

“stupid” means it’s pretty basic. Could be more sophisticated and useful. I’ll take your cue – it you find this is a good workflow companion I might revisit it.

You can use whatever delimiters you want, just first make a one-time change to the AppleScript source code in those two properties, compile, and save the script. For example you might prefer:



I understand the “basic/stupid” issue but for me, on my ‘basic’ level, anything is a very smart thing! :smiley:

Certainly this script will be a good companion to my workflow - it’s a way to solve the lack of tag feature in Editorial/ios.

The example “” is a good choice (a kind of ignored text on markdown preview). The Ulysses consideration of these kind of syntax as a “~Raw Source~” isn’t a problem on synced work between DT, Editorial and Ulysses?


Turns out that the begin/end delimiters can be the same after all:

~blueberry buckle~

works just fine. Be sure to declare in your script that

property beginTag : "~"
property endTag : "~"

Thanks korm

The expression “@tag name%” works better:

  • “” doesn’t work and generates some conflicts between DT and Ulysses (changing “” for “<tag name>”)
  • “~tag name~” doesn’t work well - if open before in Ulysses, this app change the expression to “~tag name~” and then, with the script on DT, we have tags like this: “tag name”

For now the first text delimiters work better and with the ‘snippets’ in Editorial it’s not difficult to write this expression.

I use YAML constructs in my markdown documents that guarantees they’ll be parsed correctly. I picked up by publishing a website in Jekyll so I’m routinely using YAML frontmatter to designate various metadata for a document so I can manipulate it later in liquid.

e.g. my website (incumbent.org) is just markdown files and posts or pages contain YAML frontmatter that looks like

title: Document Title
layout: article
date: 2015-01-01
modified: 2016-08-12
categories: [articles, feature, photography]
tags: [photography, tool, article, software, workflow, 'multi-word stuff']
featured: true
draft: false
   thumbnail: images/something-th.jpg
   intro: images/something.jpg
license: cc-by-sa
wpm: true
related: true
generatepdf: true

YAML can be arbitrary but the format of the data/array is consistent so you have a known quantity to work with. Since a lot of the stuff I write ends up on the web via jekyll I’ve got a sanity check on valid input so I’m not likely to have any surprises. I can also add the tags from my markdown files in the YAML frontmatter into Spotlight/OS X tags by using the tag tool/command (via homebrew, homepage for code — https://github.com/jdberry/tag/) which lets you list/add/remove/edit tags in the Spotlight metadata; so it’s easy to pull the tags array out of a markdown file and send that to tag and DEVONthink can deal with it.

Worth pointing out that some file sync software doesn’t handle Spotlight metadata very well or completely. Arq the backup software is 100% across the board and Dropbox respects and handles macOS tags just fine but I don’t know about how comprehensive it is. BitTorrent Sync usually does but there are some caveats. OneDrive and Google Drive last time I checked are completely ignorant of it and blow it up (but doesn’t matter in DEVONthink Sync because DEVONthink handles that sort of metadata in their process and doesn’t rely on the underlying filesystem to do it.)

I hope this wasn’t too much of a tangent or irrelevant.

This is simple script that works well here to accomplish OP’s stated purpose, extract single-word tags prefixed by @ and add them to DT.

-- extract single-word tags in form @tag1 @tag2 from indexed file and add to DT

set text item delimiters to {" ", return, linefeed}
tell application id "DNtp"
	repeat with aRec in (get selection)
		set ii to (get aRec's plain text)'s text items
		repeat with i in ii
				if i's text 1 is "@" then ¬
					set aRec's tags to aRec's tags & ¬
						i's texts 2 thru -1
			end try
		end repeat
	end repeat
end tell

Hi @korm

Thank you for this! Question for you: I have my Roam Research Graph indexed as .md files in a folder in one of my DT databases. Every .md file has a line that looks like this:

Tags:: #[[tag1]] #[[tag2]] #[[tag3]]

That is, the line always begins with “Tags::” Is there a way for this script to only create DT tags from this single line of tags in each file?

Reason: All the backlinks in my md pages have the same prefix/suffix, but I only want the ones in the “Tags:” line to be converted into DT tags; otherwise every one of my .md files in DT will have a bajillion DT tags.

Thanks again!

Hi – sorry, I don’t rewrite these scripts any more. But I’d suggest you might want to experiment with Preferences > Files > Import > Tags > Convert hashtags to tags, which might do what you want without any further coding.

(Why are you embedding backlinks within tags?)

Thanks! I’ll try to figure it our.

I use backlinks within tags because some tags have multiple words. Functionally, in Roam, tags and backlinks are functionally the same (tags are also backlinks), but I personally apply them in different ways (e.g. tags are never in the middle of a block of text).