4

I have a function

f(){
    echo 777
}

and a variable to which I assign the "return value" of the function.

x=$(f)

Very concise! However, in my real code, the variable and function names are quite a bit longer and the function also eats positional arguments, so that concise line above, gets very long. Since I like to keep things tidy, I would like to break the code above in two lines.

x=\
$(f)

Still works! But: keeping things tidy also means respecting the indentation, so that gives something like

if foo
    x=\
    $(f)
fi

which does not work anymore due to whitespaces! Is there a good workaround for this?

pfnuesel
  • 5,837

4 Answers4

11

Why go for complex, hard-to-read constructs? There is a perfectly natural way to present this which doesn't need any intermediate assignments, fancy ways of building an empty string, quoting subtleties or other cognitive burden.

if foo; then
    x=$(
      a_very_long_command_name --option1='argument 1 is long' \
                               --option2='argument 2 is long as well'
    )
fi
3

You can store the value in $_, which is set to the last argument:

if foo; then
    : "$(f)"
    x=$_
fi

Or can use a subshell to eat the indent:

if foo; then
    x=$(
    )$(f)
fi
Crestwave
  • 178
  • 1
    The second and third solutions are nice! The first one not so much. I assume this is why you got a downvote, which is completely unnecessary. Thanks for your help! – pfnuesel Oct 31 '18 at 07:15
  • @pfnuesel It's a long story; basically, I thought of it first, posted it, realized the problem and added the note, got downvoted while thinking/searching for other solutions, then added the others. I'm not entirely sure why I didn't delete it after that (partially because the subshell was unquoted, anyway, I guess), but I've removed it now. Thanks for the accept! – Crestwave Oct 31 '18 at 08:11
  • Why use two subshells if you already have one? – Ruslan Oct 31 '18 at 10:51
  • @Ruslan Well, it crossed my mind, but then it slipped right out of it. You want Gilles' answer. – Crestwave Oct 31 '18 at 12:16
2

If you are allowed to use here-docs, the following style works good. Quoting the here-doc string with a leading - allows your code to be intended with tabs only.

Something like

if true; then
        read -d '' -r x <<-EOF
        $(f)
        EOF
fi

But remember copy pasting the code from above doesn't work as Stack Exchange replaces tabs with spaces. You need to carefully type in the Tab character for the lines starting with the here-doc and the lines ending the here-doc. My vim configuration has mapped the tab character to 8 spaces. If you want to make it even neater, modify the spacing rule in vim by setting the spacing for tab to 4 spaces as :set tabstop=4

You can see how the Tab is formatted in my script, by looking into it using sed

$ sed -n l script.sh
#!/usr/bin/env bash$
$
$
f(){$
    echo 777$
}$
$
if true; then$
\tread -d '' -r x <<-PERSON$
\t$(f)$
\tPERSON$
fi$
$
echo $x$

Notice the \t characters in the here-doc string above. If your script looks any different than the above, you would see the whining unexpected EOF errors.

Inian
  • 12,807
2

Why split the line at the equals sign? You can just set the arguments to the function in a separate variable:

unset args
args+='arg1 '
args+='arg2 '
args+='arg3 '
x=$(f $args)
ewatt
  • 463