Using org-mode outside of Emacs - sort of

| categories: orgmode, emacs | tags:

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.
     (org-babel-do-load-languages
      'org-babel-load-languages
      '((python . t)))
     (let ((org-confirm-babel-evaluate nil))
       (org-babel-execute-src-block))))

org-babel-tangle

./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 .

Copyright (C) 2014 by John Kitchin. See the License for information about copying.

org-mode source

Org-mode version = 8.2.6

Discuss on Twitter