July 22, 2020

Emacs Orgmode Source Code Blocks 2

Intro

This is the third post in a series about Emacs Org mode and the second about source code blocks.

The last post about source code blocks (blocks from now on) covered org document variables, named blocks and combining blocks.

This post will focus on how and where block result output will end up. It won't cover all of the arguments and features as I don't use all of them. If you wanna get the full picture, here is the docs.

Versions

Since last post a new Emacs version 27.1 have been released! Here are some about the release, news.

I haven't covered this type of blocks before. They are called "inline code block" and is very useful when doing one liners that returns a single value. The documentation on inline code blocks can be found here.

Here are the "inline code blocks" used to show what versions of the software I am currently using.

  src_emacs-lisp{emacs-version} {{{results(=27.1=)}}}
  src_emacs-lisp{org-version} {{{results(=9.4=)}}}

Default argument

Results from source block evaluation can appear in many different shapes. As list, as source block, as "raw", to file and so on.

This is how it end up if no argument if provided.

  #+BEGIN_SRC shell
    echo "Hello Emacs"
  #+END_SRC

  #+RESULTS:
  : Hello Emacs

This is the same as the argument :results output.

  #+BEGIN_SRC shell :results output
    echo "Hello Emacs"
  #+END_SRC

  #+RESULTS:
  : Hello Emacs

Output code

Lets output the result into a new source code block. By default the new block gets the same language as the parent. In this case the language is shell.

  #+BEGIN_SRC shell :results output code
    echo "Hello Emacs"
  #+END_SRC

  #+RESULTS:
  #+begin_src shell
  Hello Emacs
  #+end_src

If the output language is not the same as the parent. :wrap can be used to tell what language the result block will get.

  #+BEGIN_SRC shell :results output code :wrap src text
    echo "Hello Emacs"
  #+END_SRC

  #+RESULTS:
  #+begin_src text
  Hello Emacs
  #+end_src

Append results

Results can be appended into the org document. In this case the block have been evaluated three times. I don't use this very often.

  #+BEGIN_SRC shell :results output append
    date +'%Y-%m-%d %H:%M:%S'
  #+END_SRC

  #+RESULTS:
  : 2020-08-29 11:52:34
  : 2020-08-29 11:52:36
  : 2020-08-29 11:52:38

Table

Results can end up in nicely formatted org tables

  #+BEGIN_SRC shell :results table
    ps -a
  #+END_SRC

  #+RESULTS:
  |     PID | TTY   |     TIME | CMD      |
  |    5456 | tty1  | 00:00:00 | swaybg   |
  |    5468 | tty1  | 00:00:49 | swaybar  |
  |    5479 | tty1  | 00:01:12 | sh       |
  |    5937 | tty1  | 00:07:14 | Xwayland |
  | 1279652 | pts/0 | 00:00:03 | hugo     |

Or what about a nice looking NetworkManager table.

  #+BEGIN_SRC shell
    nmcli -g name,type,device con | awk 'BEGIN{FS=":"; OFS="\t"};{print $1, $2, $3}'
  #+END_SRC

  #+RESULTS:
  | iPhone             | 802-11-wireless | wlp0s20f3     |
  | integrity_vpn      | wireguard       | integrity_vpn |

File

Results can be written to a file. In the results area a org link to the file containing the results is provided.

  #+BEGIN_SRC shell :results file :file script.sh
  echo "#!/bin/bash"
  echo "echo Hey"
  #+END_SRC

  #+RESULTS:
  [[file:script.sh]]

Here is a larger case. Write the results to a file. The filename should be script.sh and be written in folder /tmp. The file should also have execute permissions for all three user types. This :file-mode argument came in Org version 9.4.

  #+HEADER: :results file
  #+HEADER: :file script.sh
  #+HEADER: :output-dir /tmp
  #+HEADER: :file-mode (identity #o777)
  #+BEGIN_SRC emacs-lisp
    "#!/bin/bash\necho Hello World"
  #+END_SRC

  #+RESULTS:
  [[file:/tmp/script.sh]]

Let's look on the attributes of the script.sh file.

  #+BEGIN_SRC shell :results output code :dir /tmp
    ls -la | grep script.sh
  #+END_SRC

  #+RESULTS:
  #+begin_src shell
  -r-xr-xr-x  1 nils users   28 Aug 29 11:59 script.sh
  #+end_src

Pass result on

Result data from one source block can be passed to another in the same execution. You don't need to run C-c C-c two times and have results stored in variables. In the example on bash block generates a string and numbers from 1 to 20. Those two lines are then passed on to the attr_wrap named source block. This block takes data from the previous block and append two more lines. One with a string and the other with numbers from 20 to 30.

  #+NAME: attr_wrap
  #+BEGIN_SRC shell :var data="" :results raw
    echo -n "$data"
    echo "... and this appends this"
    echo {20..30}
  #+END_SRC

  Evaluate the block below, the upper block will be called
  automatically.

  #+HEADER: :post attr_wrap(data=*this*)
  #+HEADER: :file /tmp/a.txt
  #+HEADER: :results output code
  #+BEGIN_SRC shell
    echo "This blocks generates this data"
    echo {01..10}
  #+END_SRC

  #+RESULTS:
  #+begin_src shell
  This blocks generates this data
  01 02 03 04 05 06 07 08 09 10
  ... and this appends this
  20 21 22 23 24 25 26 27 28 29 30
  #+end_src

Tangle

This is a sidestep from source code block results, but it's related. Text within a source code block can be written to a file. Using the (org-babel-tangle) function. (org-babel-tangle) will look through all of the document and find all of the source code blocks with :tangle arguments and write all of them to the local file system.

This example writes some text from a source code block to a file.

  #+BEGIN_SRC text :tangle /tmp/hey-from-org.txt
    This is some text
  #+END_SRC

  #+BEGIN_SRC shell :results output code
    cat /tmp/hey-from-org.txt
  #+END_SRC

  #+RESULTS:
  #+begin_src shell
  This is some text
  #+end_src

Tangled file permissions

Just lite :file-mode we have :tangle-mode to set file permissions when writing a file to the file system. This example is a small bash script written to the file system and set to be executable for all users.

  #+HEADER: :tangle /tmp/hey-org-mode.sh
  #+HEADER: :tangle-mode (identity #o777)
  #+BEGIN_SRC shell
    #!/bin/bash
    echo "Hello World"
  #+END_SRC

  Lets check what permissions the file got after tangled.

  #+BEGIN_SRC shell :results output code
    ls -la /tmp | grep hey-org-mode.sh
  #+END_SRC

  The file got *read*, *write* and *execute* for everyone. That's what
  we told in the =:tange-mode= header.

  #+RESULTS:
  #+begin_src shell
    -rwxrwxrwx  1 john users      31 Jul 22 11:40 hey-org-mode.sh
  #+end_src

Bigger example

This example combines many of the features. And in the end it generates a nicely formatted system report with a org structure.

Lets start by specifying the commands to run in the report.

  #+NAME: report-commands
  | Disk usage    | df -h      |
  | Memory usage  | free -h    |
  | Network       | nmcli conn |
  | System time   | date       |
  | System uptime | uptime     |
  | System kernel | uname -a   |

Run the table into a awk source code block to format it into a bash script. The result is written to a file and execute permissions are set on the file.

  #+HEADER: :stdin report-commands
  #+HEADER: :results file
  #+HEADER: :file /tmp/system-report.sh
  #+HEADER: :file-mode (identity #o744)"
  #+BEGIN_SRC awk
    BEGIN {
        FS="\t"
        print "#!/bin/bash\n"
        print "echo \"#+TITLE: $1\""
        print "echo \"#+DATE: $(date +'%m-%d-%Y %H:%M:%S')\""
        print "echo \"#+STARTUP: overview\""
        print "echo \"\""
    }
    {
        printf("echo \"* %s\"\n", $1)
        printf("echo \"\"\n")
        printf("echo \"#+BEGIN_SRC shell\"\n")
        printf("echo \"%s\"\n", $2)
        printf("echo \"#+END_SRC\"\n")
        printf("echo \"\"\n")
        printf("echo \"#+RESULTS:\"\n")
        printf("echo \"#+BEGIN_SRC text\"\n")
        printf("echo \"$(%s)\"\n", $2)
        printf("echo \"#+END_SRC\"\n")
        printf("echo \"\"\n")
    }
  #+END_SRC

  #+RESULTS:
  [[file:/tmp/system-report.sh]]

The output of the system-report.sh will be pretty flat and will not look nice even in org. Lets format it with some level of indentation depending on context. This source block is only responsible for formatting and cant do anything on it's own.

  #+NAME: org_format
  #+HEADER: :var data=""
  #+HEADER: :results raw
  #+BEGIN_SRC shell
    echo -n "$data" \
        | awk '$1~/BEGIN_SRC|RESULTS/,$1~/END_SRC/ { print "   ", $0; next }; { print }' \
        | awk '$1~/^#+/ {gsub(/^[ \t]+/, "  ", $0) ; print; next}; { print }'
  #+END_SRC

Let run the script and pass it though the format source code block.

  #+HEADER: :dir /tmp
  #+HEADER: :post org_format(data=*this*)
  #+HEADER: :results output code
  #+HEADER: :wrap src org
  #+BEGIN_SRC shell
    ./system-report.sh "Daily system report"
  #+END_SRC

And voila, here is the result! The report is well formatted and looks very good.

  #+TITLE: Daily system report
  #+DATE: 10-11-2020 21:06:41
  #+STARTUP: overview

  * Disk usage

    #+BEGIN_SRC shell
      df -h
    #+END_SRC

    #+RESULTS:
    #+BEGIN_SRC text
      Filesystem             Size  Used Avail Use% Mounted on
      dev                     16G     0   16G   0% /dev
      run                     16G  1.2M   16G   1% /run
      /dev/mapper/cryptroot  468G   27G  418G   6% /
      tmpfs                   16G  187M   16G   2% /dev/shm
      tmpfs                  4.0M     0  4.0M   0% /sys/fs/cgroup
      tmpfs                   16G  2.4M   16G   1% /tmp
      tmpfs                  3.2G  8.7M  3.1G   1% /run/user/1000
    #+END_SRC

  * Memory usage

    #+BEGIN_SRC shell
      free -h
    #+END_SRC

    #+RESULTS:
    #+BEGIN_SRC text
      total        used        free      shared  buff/cache   available
      Mem:           31Gi       1.7Gi        25Gi       621Mi       3.8Gi        28Gi
      Swap:            0B          0B          0B
    #+END_SRC

  * Network

    #+BEGIN_SRC shell
      nmcli conn
    #+END_SRC

    #+RESULTS:
    #+BEGIN_SRC text
      NAME                UUID                                  TYPE       DEVICE
      iPhone              22f467bf-5d07-41d1-a394-a7b778c4ceb2  wifi       wlp0s20f3
      integrity_vpn       da1a14fe-ba3e-4251-bc98-03d604ec4fe0  wireguard  integrity_vpn
    #+END_SRC

  * System time

    #+BEGIN_SRC shell
      date
    #+END_SRC

    #+RESULTS:
    #+BEGIN_SRC text
      Sun Oct 11 09:06:42 PM CEST 2020
    #+END_SRC

  * System uptime

    #+BEGIN_SRC shell
      uptime
    #+END_SRC

    #+RESULTS:
    #+BEGIN_SRC text
      21:06:42 up 1 day,  2:26,  1 user,  load average: 0.56, 0.63, 0.58
    #+END_SRC

  * System kernel

    #+BEGIN_SRC shell
      uname -a
    #+END_SRC

    #+RESULTS:
    #+BEGIN_SRC text
      Linux arch 5.8.14-arch1-1 #1 SMP PREEMPT Wed, 07 Oct 2020 23:59:46 +0000 x86_64 GNU/Linux
    #+END_SRC

Powered by Hugo & Kiss.