## Using org-mode outside of Emacs - sort of

| categories: | tags: | View Comments

I recently posted about using Emacs for scripts (http://kitchingroup.cheme.cmu.edu/blog/2014/08/06/Writing-scripts-in-Emacs-lisp/ ). Someone was probably wondering, why would you do that, when you could use shell, python or perl? A good reason is to write scripts that can access data or code inside an org-file! This would allow you to leverage the extensive support for org-mode in Emacs, without a user necessarily even needing to use Emacs. Let us consider some examples.

## 1 Extracting tables from an org-file

If tables are named in org-mode, it is possible to extract the contents. Here is a table:

x y
1 1
2 4
3 9

Another table might look like

 a b 1 1 2 8 3 27

It would be convenient to have a command-line utility that could extract the data from that table with a syntax like:

extract-org-table tblname orgfile --format lisp|csv|tab


Here is one way to do it:

;; org-table tblname orgfile lisp|csv|tab

(let ((tblname (pop command-line-args-left))
(org-file (pop command-line-args-left))
(format)
(table)
(content))
(when command-line-args-left
(setq format (pop command-line-args-left)))
(find-file org-file)
(setq table
(org-element-map (org-element-parse-buffer) 'table
(lambda (element)
(when (string= tblname (org-element-property :name element))
element))
nil ;info
t )) ; first-match

(unless table
(error "no table found for %s" tblname))

(when table
(goto-char (org-element-property :contents-begin table))
(let ((contents (org-table-to-lisp)))
(if (string= format "lisp")
(print contents)
;else
(dolist (row contents)
(unless (eq row 'hline)
(cond
((string= format "csv")
(princ (mapconcat 'identity row ",")))
((string= format "tab")
(princ (mapconcat 'identity row "\t")))
(t
(error "unsupported format: %s" format)))
(princ "\n")))))))


Let us try it out. org-babel-tangle

./extract-org-table data-2 org-outside-emacs.org lisp

(("a" "b") ("1" "1") ("2" "8") ("3" "27"))

./extract-org-table data-1 org-outside-emacs.org csv

x,y
1,1
2,4
3,9

./extract-org-table data-2 org-outside-emacs.org tab

a       b
1       1
2       8
3       27


That looks pretty reasonable, and you could even pipe the output to another classic unix command like cut to get a single column. Let us get the second column here.

./extract-org-table data-1 org-outside-emacs.org csv | cut -d , -f 2

y
1
4
9


That is starting to look like using data from an org-file, but outside of org. Of course, we are using org-mode, via emacs, but the point is a user might not have to know that, as long as a fairly recent Emacs and org-mode was installed on their system.

## 2 Running code in an org-file

It may be that there is code in an org-file that you might want to use, but for some reason choose not to cut and paste from the org-file to some script. Here is a simple code block:

import time
with open('results.dat', 'w') as f:
f.write(time.asctime())


To call this externally we have to find the block and then run it.

;; org-run blockname org-file
;; run a code block in an org file
(let ((blockname (pop command-line-args-left))
(org-file (pop command-line-args-left))
(src))
(find-file org-file)
(setq src
(org-element-map (org-element-parse-buffer) 'src-block
(lambda (element)
(when (string= blockname (org-element-property :name element))
element))
nil ;info
t )) ; first-match
(when src
(goto-char (org-element-property :begin src))
;; since we start with a fresh emacs, we have to configure some things.
'((python . t)))
(let ((org-confirm-babel-evaluate nil))
(org-babel-execute-src-block))))

./org-call.el python-block org-outside-emacs.org
cat results.dat

Mon Aug 11 20:17:01 2014


That demonstrates it is possible to call source blocks, but this is pretty limited in capability. You can only call a block; we did not capture any output from the block, only its side effects, e.g. it changed a file that we can examine. We have limited capability to set data into the block, other than through files. It might be possible to hack up something that runs org-babel-execute-src-block with constructed arguments that enables something like a var to be passed in. That is beyond today's post. When I get around to it, here is a reminder of how it might be possible to feed stdin to an emacs script: http://stackoverflow.com/questions/2879746/idomatic-batch-processing-of-text-in-emacs .