简体   繁体   中英

Bash string interpolation without subshell

I have a function like this

print_stuff_and_set_vars() {
  IMPORTANT_VAL_1=""
  IMPORTANT_VAL_2=""
  echo -n "some stuff"
  echo -n "some more stuff"
  echo -n "result"
}

and I call it like this:

my_main_func() {
  print_stuff_and_set_vars
  print_stuff_and_set_vars
  print_stuff_and_set_vars
  echo "IMPORTANT_VAL_1 was $IMPORTANT_VAL_1"
}

Instead, I want to save all the echoed results to a string

my_main_func() {
  # doesn't work -- result is empty
  result="${print_stuff_and_set_vars}${print_stuff_and_set_vars}${print_stuff_and_set_vars}"
  echo "the result length was ${#result}"
  echo "$result"
  echo "IMPORTANT_VAL_1 was $IMPORTANT_VAL_1"
}

This does work if I use $() instead of ${} to start a subshell, but then the global variables are not set.

Is there any way to save the result of a function to a string without starting a subshell? I know the obvious answer in this example would be to save "result" to a global variable instead of echoing it but in my actual script that would require a lot of changes and I want to avoid it if possible.

Actually, I only need to know the length so if there is a way to keep track of how much has been printed since the start of the function that would work fine too. I'm actually using zsh if that makes a difference, too.

Assuming the output from the function print_stuff_and_set_vars does not contain newline characters, how about:

mkfifo p                        # create a named pipe "p"
exec 3<>p                       # open fd 3 for both reading and writing
rm p                            # now "p" can be closed

print_stuff_and_set_vars() {
  IMPORTANT_VAL_1="foo"
  IMPORTANT_VAL_2="bar"
  echo -n "some stuff "
  echo -n "some more stuff "
  echo -n "result "
}

my_main_func() {
  print_stuff_and_set_vars 1>&3 # redirect to fd 3
  print_stuff_and_set_vars 1>&3 # same as above
  print_stuff_and_set_vars 1>&3 # same as above
  echo 1>&3                     # send newline as an end of input

  IFS= read -r -u 3 result      # read a line from fd 3
  echo "the result length was ${#result}"
  echo "$result"
  echo "IMPORTANT_VAL_1 was $IMPORTANT_VAL_1"
}

my_main_func

exec 3>&-                       # close fd 3

Output:

the result length was 102
some stuff some more stuff result some stuff some more stuff result some stuff some more stuff result
IMPORTANT_VAL_1 was foo

This is very easy to do with temp file. Example:

print_stuff_and_set_vars() {
  IMPORTANT_VAL_1="x"
  IMPORTANT_VAL_2="y"
  echo -n "some stuff"
  echo -n "some more stuff"
  echo -n "result"
}

print_stuff_and_set_vars > /tmp/$$
myVar=$(< /tmp/$$)
echo $myVar 
echo $IMPORTANT_VAL_1
echo $IMPORTANT_VAL_2

It is possible to implement this using named pipe or file descriptor as well.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM