ob-shell.el: New option `ob-shell-return-value-is-exit-status'

* lisp/ob-shell.el (ob-shell-return-value-is-exit-status): New
option.
(org-babel-execute:shell, org-babel-sh-evaluate): Use it.

In a shell source code block, when there is no :results header or when
the :results header is "value", the code block should return the value
of the source block, called in "functional mode", i.e. with the code
being called as a function and returning a value.  See this part of
the manual:

‘value’
     Default.  Functional mode.  Org gets the value by wrapping the code
     in a function definition in the language of the source block.  That
     is why when using ‘:results value’, code should execute like a
     function and return a value.  For languages like Python, an
     explicit ‘return’ statement is mandatory when using ‘:results
     value’.  Result is the value returned by the last statement in the
     code block.

Strictly speaking, the "return value" of a shell source code block
should be the exit status of the last command in the block.  As the
manpage for bash says: "the return value of a simple command is its
status".

This patch allows this strict interpretation of "value" for shell
blocks, while sticking to the current behavior of returning the shell
output of the code block.

Thanks to Vladimir Nikishkin for asking a question about this and to
Eric Fraga and Tim Cross for their inputs on this issue.
This commit is contained in:
Bastien 2020-02-19 16:42:05 +01:00
parent 4f31aa2d3b
commit 4533d783c2
1 changed files with 21 additions and 4 deletions

View File

@ -71,6 +71,16 @@ outside the Customize interface."
(set-default symbol value)
(org-babel-shell-initialize)))
(defcustom ob-shell-return-value-is-exit-status nil
"Should we consider the shell exit status as the return value?
When this is set to nil (the default), consider that the return
value of a shell source block is the output of the commands.
Otherwise, consider the return value to be the exit status of the
last command of the block."
:group 'org-babel
:type 'boolean
:package-version '(Org . "9.4"))
(defun org-babel-execute:shell (body params)
"Execute a block of Shell commands with Babel.
This function is called by `org-babel-execute-src-block'."
@ -79,9 +89,13 @@ This function is called by `org-babel-execute-src-block'."
(stdin (let ((stdin (cdr (assq :stdin params))))
(when stdin (org-babel-sh-var-to-string
(org-babel-ref-resolve stdin)))))
(value-is-exit-status (or (cdr (assq :value-is-exit-status params))
ob-shell-return-value-is-exit-status))
(cmdline (cdr (assq :cmdline params)))
(full-body (org-babel-expand-body:generic
body params (org-babel-variable-assignments:shell params))))
(full-body (concat
(org-babel-expand-body:generic
body params (org-babel-variable-assignments:shell params))
(when value-is-exit-status "\necho $?"))))
(org-babel-reassemble-table
(org-babel-sh-evaluate session full-body params stdin cmdline)
(org-babel-pick-name
@ -209,6 +223,8 @@ If RESULT-TYPE equals `output' then return a list of the outputs
of the statements in BODY, if RESULT-TYPE equals `value' then
return the value of the last statement in BODY."
(let* ((shebang (cdr (assq :shebang params)))
(value-is-exit-status (or (cdr (assq :value-is-exit-status params))
ob-shell-return-value-is-exit-status))
(results
(cond
((or stdin cmdline) ; external shell script w/STDIN
@ -260,8 +276,9 @@ return the value of the last statement in BODY."
(insert body))
(set-file-modes script-file #o755)
(org-babel-eval script-file "")))
(t
(org-babel-eval shell-file-name (org-trim body))))))
(t (org-babel-eval shell-file-name (org-trim body))))))
(when value-is-exit-status
(setq results (car (reverse (split-string results "\n" t)))))
(when results
(let ((result-params (cdr (assq :result-params params))))
(org-babel-result-cond result-params