如何将命令的输出存储在变量中而不创建子壳[bash< v4]



ksh 有一个非常有趣的构造可以做到这一点,在此答案中详细介绍:https://stackoverflow.com/a/11172617/636849

自Bash 4.0以来,有一个内置的 mapfile 内置命令,该命令应该解决此问题:http://www.gnu.org/software/bash/manual/html_node/bash-builtins.html

,但奇怪的是,它似乎与流程替换不起作用:

foo () { echo ${BASH_SUBSHELL}; }
mapfile -t foo_output <(foo) # FAIL: hang forever here
subshell_depth=${foo_output[0]} # should be 0

但是如何在bash v3.2?

中执行此操作

这是另一种做到这一点的方法,它足够不同,可以单独答案。我认为此方法不含子壳,bash子过程免费:

ubuntu@ubuntu:~$ bar () { echo "$BASH_SUBSHELL $BASHPID"; }
ubuntu@ubuntu:~$ bar
0 8215
ubuntu@ubuntu:~$ mkfifo /tmp/myfifo
ubuntu@ubuntu:~$ exec 3<> /tmp/myfifo
ubuntu@ubuntu:~$ unlink /tmp/myfifo
ubuntu@ubuntu:~$ bar 1>&3
ubuntu@ubuntu:~$ read -u3 a
ubuntu@ubuntu:~$ echo $a
0 8215
ubuntu@ubuntu:~$ exec 3>&-
ubuntu@ubuntu:~$

这里的诀窍是使用exec在使用FD的读写模式下打开FIFO,这似乎具有使FIFO非阻滞的副作用。然后,您可以将命令重定向到FD而不会阻止它,然后读取FD。

请注意,FIFO将是一个有限尺寸的缓冲区,可能大约是4K,因此,如果您的命令产生的输出比这更多,则最终会再次阻止。

这个问题经常出现,同时寻找如何将任何"打印"命令的输出捕获到变量中。因此,对于任何人来说,都有可能(因为bash v3.1.0)与:

printf -v VARIABLE_NAME "whatever you need here: %s" $ID

如果您以速度调整脚本,则可以使用在功能末尾设置某些全局变量的模式,而不仅仅是"回声"它 - 谨慎使用它,有时会批评它导致很难维护代码。<<<<<<<<<<<<

这是我可以提出的 - 它有点混乱,但是foo在顶级壳上下文中运行,并且在顶级壳中的可变a中提供了其输出上下文:

#!/bin/bash
foo () { echo ${BASH_SUBSHELL}; }
mkfifo /tmp/fifo{1,2}
{
    # block, then read everything in fifo1 into the buffer array
    i=0
    while IFS='' read -r ln; do
        buf[$((i++))]="$ln"
    done < /tmp/fifo1
    # then write everything in the buffer array to fifo2
    for i in ${!buf[@]}; do
        printf "%sn" "${buf[$i]}"
    done > /tmp/fifo2
} &
foo > /tmp/fifo1
read a < /tmp/fifo2
echo $a
rm /tmp/fifo{1,2}

这当然假设有两件事:

  • 允许FIFO
  • 进行缓冲的命令组被允许放入背景

我对此进行了测试以在这些狂欢版本中工作:

  • 3.00.15(1)-Release(x86_64-redhat-linux-gnu)
  • 3.2.48(1)-Release(x86_64-apple-darwin12)
  • 4.2.25(1)-Release(x86_64-pc-linux-gnu)

附录

我不确定Bash 4.x中的mapfile方法可以执行您想要的操作,因为该过程替换<()会创建一个全新的bash过程(尽管不是该bash过程中的bash子壳):

$ bar () { echo "$BASH_SUBSHELL $BASHPID"; }
$ bar
0 2636
$ mapfile -t bar_output < <(bar)
$ echo ${bar_output[0]}
0 60780
$ 

因此,当$BASH_SUBSHELL在这里为0时,这是因为它在该过程替换中的新壳过程60780的最高级别。

最简单的方法是删除函数并直接传递变量,例如:

declare -a foo_output
mapfile -t foo_output <<<${BASH_SUBSHELL}
subshell_depth=${foo_output[0]} # Should be zero.

否则在功能中给出了两个项目:

foo () { echo "$BASH_SUBSHELL $BASHPID"; }

您可以像以下命令之一一样使用read(根据需要修改IFS):

cat < <(foo) | read subshell_depth pid # Two variables.
read -r subshell_depth pid < <(foo) # Two separate variables.
read -a -r foo_arr < <(foo) # One array.

或使用readarray/mapfile(bash> 4):

mapfile -t foo_output < <(foo)
readarray -t foo_output < <(foo)

然后将输出转换回数组:

foo_arr=($foo_output)
subshell_depth=${foo_arr[0]} # should be 0

相关内容

  • 没有找到相关文章