3

Taking how reference the following code for simplicity

#!/bin/bash

number=7

function doSomething() { number=8 }

doSomething echo "$number"

It prints 8.

But with:

#!/bin/bash

number=7

function doSomething() { number=8 }

$(doSomething) echo "$number"

It prints 7.

I have the following questions:

  • What are the technical names for each one?, I mean functioncall and $(functioncall)
  • How does work each approach? It seems the former considers (affects) the variables outside of the function itself, the latter not
  • When is mandatory use one approach over the other (it mostly about performance issues - of course - if there is any), if there are other reasons, they are welcome.
Manuel Jordan
  • 1,728
  • 2
  • 16
  • 40

1 Answers1

10

You are experiencing the subtleties of the Command Substitution.

The call

doSomething

is a simple function call. It executes the function, pretty much as if you had copy-and-pasted the commands of the function to the place where you call it. Hence, it overwrites the variable number with the new value 8.

The call

$(doSomething)

on the other hand is a command substitution. It is meant to execute the function and return whatever the function printed to stdout. It is usually not used "standalone", but in variable assignments, e.g.,

os_type=$(uname)

That will execute the command uname, which on a Linux system would print Linux to the console, and store its result to the shell variable os_type. Therefore, it doesn't make sense to use a command substitution with a command or function that doesn't output anything, like your doSomething. Indeed, since the substitution $(doSomething) is basically a placeholder for the output of doSomething, the only reason why you don't get a script error there is that your function doesn't output anything. Had you stated, e.g.,

$(uname)

instead of

$(doSomething)

your shell would have tried to execute the command Linux and generated a

Linux: No such file or directory

error(1).

The key point to understand the effect you observe is that in a command substitution, the command is executed in a subshell, i.e. any changes to variables made are not backpropagated to the shell where you execute the main script. Therefore, while internally it runs the commands of doSomething and sets a variable number to 8, it does so in its own shell process that has nothing to do with the shell process that runs your script (save for the fact that its stdout is being retrieved), and therefore cannot modify the variable number you used in the main script.

For further reading, you may want to look at

here on this site, or

for more overview.


(1) On the other hand, this means you can use a command substitution to execute a command whose name you do not know at the time of writing the script, but that you can find out by executing another command you do know.

AdminBee
  • 22,803
  • Huge Thanks - for command substitution is mandatory use echo >&2 ... within the functions called – Manuel Jordan Jun 15 '21 at 15:29
  • 1
    @ManuelJordan If you want to catch error messages printed by the command, yes - you have to redirect stderr to stdout. – AdminBee Jun 15 '21 at 15:39
  • Yes, English is not my native speaking, my point was, if I have a function being called by command substitution and the called function needs print any data (either informative or error) in the console, so I need use echo >&2 ... - otherwise without >&2 does not work – Manuel Jordan Jun 15 '21 at 16:18