Bash 没有子shell的字符串插值

Bash string interpolation without subshell

我有这样的功能

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

我这样称呼它:

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"
}

相反,我想将所有回显结果保存到一个字符串

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"
}

如果我使用 $() 而不是 ${} 来启动子 shell,那么 确实 工作,但是没有设置全局变量。

有没有什么方法可以在不启动子 shell 的情况下将函数的结果保存到字符串中?我知道这个例子中明显的答案是将“结果”保存到全局变量而不是回显它,但在我的实际脚本中这需要大量更改,我想尽可能避免它。

实际上,我只需要知道长度,所以如果有一种方法可以跟踪自函数开始以来打印了多少,那也可以正常工作。如果这也有所不同,我实际上正在使用 zsh

使用临时文件很容易做到这一点。示例:

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

也可以使用命名管道或文件描述符来实现。

假设函数 print_stuff_and_set_vars 的输出不包含换行符,那么:

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

输出:

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