如何将多行 bash 命令重定向到变量中

che*_*ire 1 bash shell-script

我想将此命令重定向到一个变量:

{
    printf 'scan on\n\n'
    sleep 10
    printf 'quit \n\n'  
} | bluetoothctl
Run Code Online (Sandbox Code Playgroud)

我试图这样做:

variable=$(printf 'scan on\n\n' sleep 10 printf 'quit \n\n' | bluetoothctl)
echo $variable
Run Code Online (Sandbox Code Playgroud)

但我得到空白输出。我也尝试拆分它:

variable=$(printf 'scan on\n\n' | bluetoothctl)
sleep 10
printf 'quit \n\n'| bluetoothctl
echo $variable
Run Code Online (Sandbox Code Playgroud)

但这次我只得到

Agent registered
[bluetooth]# quit
Run Code Online (Sandbox Code Playgroud)

如何将stdout运行时打印的第一个命令的输出重定向到变量中?

Sté*_*las 5

只是:

variable=$(
  {
    printf 'scan on\n\n'
    sleep 10
    printf 'quit \n\n'  
  } | bluetoothctl
)
Run Code Online (Sandbox Code Playgroud)

命令替换的内部可以是任何 shell 代码¹,不必在一行上。

在这里,您想要捕获整个管道的输出(实际上,这将是 的输出,bluetoothctl因为所有其他命令都将其标准输出重定向到通向 的管道bluetoothctl)。

您还可以bluetoothctl仅使用以下命令捕获输出:

{
  printf 'scan on\n\n'
  sleep 10
  printf 'quit \n\n'  
} | variable=$(bluetoothctl)
Run Code Online (Sandbox Code Playgroud)

但是在 中bash,要使其工作,您需要设置lastpipe选项(shopt -s lastpipe,仅适用于非交互式 shell 实例),以使最后一个管道组件不在子 shell 中运行。

但是,在这里,您不需要管道,您可以这样做:

variable=$(bluetoothctl --timeout 10 scan on)
Run Code Online (Sandbox Code Playgroud)

如果要显示变量的内容,请记住语法是:

printf '%s\n' "$variable"
Run Code Online (Sandbox Code Playgroud)

不是echo $variable。

有关更多详细信息,请访问:

这里的输出bluetoothctl包含着色转义序列以及用于清除行内容的转义序列。如果你不加$variable引号,它的内容将受到 split+glob 的影响,导致一个随机的单词列表传递给echo它将echo以空格分隔输出。因此,您将在一行中结束所有内容,并且如果最后一个单词以清晰的序列结尾(它在我的测试中\r\e[K是这样做的:回车,然后是清除到行尾序列),你只会看到一个空行。


¹尽管一些 shell 包括bash用于扼杀带有不匹配括号的代码,例如当代码包含 case 语句case $x in foo)或不平衡的注释时),shell 解析器误认为)关闭命令替换。