From 1847b5ba3d51020c44ba2388533a1a5427feadb1 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Fri, 8 Jan 2021 09:20:50 +0200 Subject: Describe multi-value return technique in Bash style guide --- doc/bash-style.cli | 91 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 88 insertions(+), 3 deletions(-) diff --git a/doc/bash-style.cli b/doc/bash-style.cli index 1b32b1a..17a850d 100644 --- a/doc/bash-style.cli +++ b/doc/bash-style.cli @@ -577,10 +577,95 @@ function dist() } \ +A function can return data in two primary ways: exit code and stdout. +Normally, exit code 0 means success and exit code 1 means failure though +additional codes can be used to distinguish between different kinds of +failures, signify special conditions, etc., see \l{#error-handing Error +Handling} for details. + +A function can also write to stdout with the result available to the caller in +the same way as from programs (command substitution, pipeline, etc). If a +function needs to return multiple values, then it can print them separated +with newlines with the caller using the \c{readarray} builtin to read them +into an indexed array, for example: + +\ +function foo () +{ + echo one + echo two + echo three +} + +foo | readarray -t r +\ + +In this case, if the function can fail, then the failure should be explicitly +checked for (either by examining \c{PIPESTATUS} or via the lack of the +result), since the error trap will not be triggered (unless the \c{pipefail} +shell option is set; see \l{#error-handing Error Handling} for details). For +example: + +\ +foo | readarray -t r + +if [ \"${PIPESTATUS[0]}\" -ne 0 ]; then + exit 1 +fi +\ + +\N|The use of the newline as a separator means that values may not contain +newlines. While \c{readarray} supports specifying a custom separator with the +\c{-d} option, including a \c{NUL} separator, this support is only available +since Bash 4.4.| + +This technique can also be extended to return an associative array by +returning the values as an indexed array and then converting them to +an associative array with \c{eval}, for example: + +\ +function foo () +{ + echo \"[a]=one\" + echo \"[b]=two\" + echo \"[c]=three\" +} + +foo | readarray -t ia + +if [ \"${PIPESTATUS[0]}\" -ne 0 ]; then + exit 1 +fi + +eval declare -A aa=(\"${ia[@]}\") +\ + +Note that if a key or a value contains whitespaces, then it must be quoted. +The recommendation is to always quote both, for example: + +\ +function foo () +{ + echo \"['a']='one ONE'\" + echo \"['b']='two'\" + echo \"['c']='three'\" +} +\ + +Or, if returning a local array: + +\ +function foo () +{ + declare -A a=([a]='one ONE' [b]=two [c]=three) + + for k in \"${!a[@]}\"; do + echo \"['$k']='${a[$k]}'\" + done +} +\ + For more information on returning data from functions, see \l{https://mywiki.wooledge.org/BashFAQ/084 BashFAQ#084}. -For more information on writing reusable functions, see -\l{https://stackoverflow.com/questions/11369522/bash-utility-script-library -Bash Utility Script Library}. " -- cgit v1.1