Post updates
- [2020-06-23] New section. "Combine source blocks"
Intro
It's time to start documenting how I use Emacs Org mode (from now on org) and it will contain a serie of posts. This is the first post and it will cover source blocks. Some of the content assumes basic knowledge about org. If you are new to org I can recommend this two videos; hrs and kitchin.
One of the features I've been using very frequently is source blocks. The power of
evaluating code directly from a block and get the results back into the document is
really convenient. While reading The AWK Programming Language I took notes and did all
of the awk
programming withing source blocks. This makes me able to have notes, code
and execution results in the same document. And as everything is just text, it's easy
to version control!
This is the structure of a source block. Org mode's documentation on source blocks can be found here.
#+BEGIN_SRC <language> <switches> <header arguments>
<body>
#+END_SRC
Header arguments differ for different languages and applications. Look in the documentation for your specific language/application to find out what and how to use it.
Versions
… of Emacs and Org mode.
#+BEGIN_SRC emacs-lisp
(org-version)
#+END_SRC
#+RESULTS:
: 9.3.7
#+BEGIN_SRC emacs-lisp
(emacs-version)
#+END_SRC
#+RESULTS:
: GNU Emacs 26.3 (build 1, x86_64-pc-linux-gnu, GTK+ Version 3.24.20)
: of 2020-05-19
Emacs config
Without config Emacs don't know how to execute source blocks. This following snippet
provides Emacs with the ability to understand a set of languages/applications. The set
of languages/applications need to be available in the underlying OS. To get a complete
list of languages/applications available in org. A complete list can be found here:
repo. Support for source blocks are available in files prefixed with ob-
.
(setq org-confirm-babel-evaluate nil)
(org-babel-do-load-languages
'org-babel-load-languages
'((emacs-lisp . t)
(shell . t) ; in my case /bin/bash
(scheme . t)
(python . t)
(ledger . t)
(sed . t)
(awk . t)
(clojure . t)))
Bash echo
This small example uses bash to evaluate the echo
command. Evaluation is done by
moving the pointer to the source block and press the key sequence C-c C-c
.
What shell am I using?
#+BEGIN_SRC shell
echo $SHELL
#+END_SRC
#+RESULTS:
: /bin/bash
Echo input variables
Arguments can be passed to source blocks from the document. This example passes 2 :var
,
ARG1 and ARG2. The command get the arguments and uses them in the command.
#+BEGIN_SRC shell :var ARG1="FOO" ARG2="BAR"
echo $ARG1 $ARG2
#+END_SRC
#+RESULTS:
: FOO BAR
Document variables
Lets elaborate on argument to source blocks even further. Org documents can contain
variables that holds data. This example contains a org table with the variable name
my-list
. my-list
is passed as an argument to the Python source block via the variable
lst
. When executed by Python the lst
data structure is a vector with vectors.
#+NAME: my-list
| A | 1 |
| B | 2 |
| C | 3 |
my-list is a variable in this org document and can be passed in as data to a
source code block.
#+BEGIN_SRC python :results output :var lst=my-list
print(lst)
#+END_SRC
The data structure that will be passed to the source block is a vector or
vectors.
#+RESULTS:
: [['A', 1], ['B', 2], ['C', 3]]
Just like with a normal Python application you can manipulate the data.
#+BEGIN_SRC python :results raw :var lst=my-list
return [[chr((ord(x)+1)), y+1] for x,y in lst]
#+END_SRC
#+RESULTS:
[['B', 2], ['C', 3], ['D', 4]]
Result output
Results output can be in many different shapes. Raw output, tables, as code and many
more. Output results are described with the :results
switch in the source block header.
This post won't go more into details but more information can be found here.
More on document variables
This example provides another org document variable. In this case the text-example
text block. The text in that block is passed as an argument to the awk
source block.
awk
is a programming language with good features to manipulate text. It can take text
from stdin. That's exactly what is happening here. Often you see awk
used in shell
commands, similar to this: cat /etc/passwd | awk -F":" '{ print $1 }'
. Here awk
gets
stdin data from the pipe. But in our source block it gets stdin data from the org
document.
#+NAME: text-example
#+BEGIN_EXAMPLE
Item1 100
Item2 200
Item3 50
#+END_EXAMPLE
Sum field nr 2.
#+BEGIN_SRC awk :results table :stdin text-example
BEGIN {OFS="|"}; { sum+= $2}; END { print "Sum", sum}
#+END_SRC
#+RESULTS:
| Sum | 350 |
File from file system
This example is very similar to the one in "Echo input variables". It passed a variable
to the source block that tells a file path to awk
. awk
is then called with that
:in-file as an argument.
Get system users that have =/bin/bash= access.
#+BEGIN_SRC awk :results table :in-file /etc/passwd
BEGIN { FS=":"}
$7 == "/bin/bash" { print $1}
#+END_SRC
#+RESULTS:
| root |
| john |
Combine source blocks
It's possible to combine text from different source blocks with the :noweb yes
header
argument. This example shows a ledger that's split up into income and expenses source
blocks. They are then combined in the balance
source block. Ledger is a plaintext
accounting tool and can create a balance report from our assets, income and expenses.
The :noweb
header arguments in not something that is specific for ledger.
Docs on :noweb
header argument can be found here. Docs on ledger can be found here.
#+NAME: income
#+BEGIN_SRC ledger
2020-06-24 * Sold old computer
Assets:Bank:Card 5000.00
Income:Secondhand -5000.00
2020-06-25 * Salary
Assets:Bank:Card 25000.00
Income:Work:Salary -25000.00
#+END_SRC
#+NAME: expenses
#+BEGIN_SRC ledger
2020-06-23 * Rent
Expenses:Rent 3000.00
Assets:Bank:Card -3000.00
2020-06-24 * Gas
Expenses:Cas:Gas 3000.00
Assets:Bank:Card -3000.00
#+END_SRC
#+NAME: balance
#+BEGIN_SRC ledger :noweb yes
<<income>>
<<expenses>>
#+END_SRC
#+RESULTS: balance
: 24000 Assets:Bank:Card
: 6000 Expenses
: 3000 Cas:Gas
: 3000 Rent
: -30000 Income
: -5000 Secondhand
: -25000 Work:Salary
: --------------------
: 0
Combine results and source blocks
Now lets use the result from one source block as argument to another. The example
requires some files with a .awk
ending to be present in /tmp
. We use bash
to get
data about our file system. That result is then feed to awk
that creates a good
looking table. That table is then sent to Python for further processing.
There is nothing different here from the other examples except that in this case we use
#+NAME:
on source blocks instead of a text blocks or a tables. The results from the
named source blocks will then be available as input to other source blocks.
List files ending with .awk in /tmp/ dir.
#+NAME: ls-output
#+BEGIN_SRC shell :dir /tmp :results output
ls -la *.awk
#+END_SRC
#+RESULTS: ls-output
: -rw-r--r-- 1 john users 492 Jun 15 11:46 awk-play.awk
: -rw-r--r-- 1 john users 396 Jun 15 13:10 copy-line-on-confirm.awk
: -rw-r--r-- 1 john users 412 Jun 15 12:25 filter-google-chromepasswords.awk
Take only size and filename and return a table.
#+NAME: ls-output-table
#+BEGIN_SRC awk :results table :stdin ls-output
BEGIN { OFS="|" }; { print $5, $9}
#+END_SRC
#+RESULTS: ls-output-table
| 492 | awk-play.awk |
| 396 | copy-line-on-confirm.awk |
| 412 | filter-google-chromepasswords.awk |
Process the table with Python.
#+BEGIN_SRC python :results output :var table=ls-output-table
print(table)
print(max(table)) # Biggest awk file
print(min(table)) # Smallest awk file
print(sum([x for x,y in table]) / 1000 , "kb") # Sum of all awk files
#+END_SRC
#+RESULTS:
: [[492, 'awk-play.awk'],
: [396, 'copy-line-on-confirm.awk'],
: [412, 'filter-google-chromepasswords.awk']]
: [492, 'awk-play.awk']
: [396, 'copy-line-on-confirm.awk']
: 1.3 kb
Nice things I didn't cover
Org have ways to execute code on remote servers via source blocks. This is awesome and can be used for DevOps. I have used it in cases where an Ansible playbook is overkill. There is a really nice blog post and video about this stuff here.
Source blocks can also be written to the file system via the header switch :tangle
. This
is also very useful. More info about that can be found here.
Conclusion
I think org is truly an amazing piece of software. With very little effort from the user it can provide a very rich and interactive experience with a lot of applications and languages. From the document view everything is just text so version control a org document is very simple.