0

Is there a reliable way to get the value of a variable defined inside an org-mode file from the UNIX shell (bash)?

For example, if I have the following inside an org mode file test.org:

#+NAME: foo
| bar |

, is it possible to write a shell script to get the value of foo?

I know how to extract the value of a variable inside the org file, but just couldn't find a way to retrieve the value externally from the command line.

(This is with the org mode shipped with Emacs 26 in Ubuntu 20.04 LTS)

  • 1
    Your example is a table named foo with a single row and column that contains bar. So I'm not sure what you mean by "extracting the value of a variable" but maybe you can clarify by showing the method you use when you are inside the Org mode file. – NickD Oct 18 at 13:02
  • @NickD Actually, I don't know org-mode enough to know how to define a variable variable. But I just wanted to collect this variable value from a shell script (e.g. a filename), and then maybe use that on a UNIX command-line (e.g. a pipe line). My question is really how to extract any scalar value from an org file by an external program (command line). In the big picture, I want to store a value in org and use it from outside. – tinlyx Oct 18 at 18:04
  • That's too general a question to attempt to answer: there are many ways to store a scalar value in an Org mode file and how you get that value depends on how you have stored it. You can e.g. use a PROPERTY - would that satisfy your requirements? – NickD Oct 18 at 18:16
  • @NickD Yes, a property would do. Thanks for being so helpful. Do you mean I can access a property from Unix shell? – tinlyx Oct 18 at 18:22
  • Sure - see my answer. But you can use other methods: you could e.g. retrieve the value from a table: you'd just have to use a different function than the one I show below (and I think it would be more complicated, but I have not tried it). – NickD Oct 18 at 21:32
2

The general answer consists of two parts: 1) you write a Lisp function that extracts the value of interest and 2) you arrange a command-line invocation that calls that function and gives you the value on its standard output, so that it can be incorporated in a shell script.

The second part is an invocation of emacsclient with the appropriate arguments. You can capture its stdout by using the $(some_command) syntax of bash (and other shells, but the exact syntax depends on the shell - I am assuming bash from now on).

E.g. assuming that you have Emacs running and that you have started a server with (server-start), the invocation

$ emacsclient -e "(+ 1 2)"
3

will send the string to the server and ask it to evaluate it. The evaluation results in the value 3 which is sent back to emacsclient and is printed on its stdout.

So a simple bash program that would make use of that output would be something like

echo The value that emacs returned was $(emacsclient -e "(+ 1 2)")

The first part involves writing a function to retrieve the value of some property that is found under some headline in some file. The function takes the pathname of the file, the headline and the property name as arguments and it should return the value of the property. The following is an initial attempt at that (it does not deal with errors, but it should be good enough to experiment with):

(defun ndk/get-headline-property-from-file (fname headline prop)
  "Open the file, search for the headline and retrieve the value
of the property. FNAME is the path of the file to be searched,
HEADLINE is the headline under which the property is looked up
and PROP is the name of the property. The value of the property
is returned.  If no value is found, the empty string is
returned (IOW, the function does not distinguish between a
non-existent property and a property whose value is empty)."
  (save-excursion
    (with-current-buffer (find-file-noselect fname)
      (goto-char (point-min))
      ;; search for the headline
      (if (re-search-forward (format org-complex-heading-regexp-format
                                     (regexp-quote headline))
                             nil t)
        (beginning-of-line)
       (goto-char (point-max)))
      ;; try to get the property
      (let ((val (org-entry-get (point) prop)))
        (if (not val)
            ""
          val)))))

Make sure that your emacs knows about this function before going further: add it to your init file and (re)start emacs. And don't forget to start the emacs server: add (server-start) to your init file as well.

Now you can put the two parts together:

echo $(emacsclient -e '(ndk/get-headline-property-from-file "./test.org" "My headline" "var1")'

with test.org (assumed to be in the current directory) containing:

* Some headline

...

* My headline
:PROPERTIES:
:var1:     baz
:END:

stuff

* Another headline
...

The echo should echo baz.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.