Bash:将管道的输出分配给变量

Bash:将管道的输出分配给变量

我正在尝试将管道的输出放入变量中。我尝试了以下操作:

echo foo | myvar=$(</dev/stdin)
echo foo | myvar=$(cat)
echo foo | myvar=$(tee)

$myvar是空空如也。

我不想做:

myvar=$(echo foo)

因为我不想生成子外壳。

有任何想法吗?

编辑:我不想生成子 shell,因为管道之前的命令需要编辑全局变量,而在子 shell 中无法执行此操作。它可以?事情echo只是为了简化。它更像是:

complex_function | myvar=$(</dev/stdin)

我不明白为什么这不起作用。这适用于例如:

complex_function | echo $(</dev/stdin)

答案1

正确的解决方案是使用如下命令替换:

variable=$(complex_command)

message=$(echo 'hello')

(或者就此而言,message=hello在本例中)。

您的管道:

echo 'hello' | message=$(</dev/stdin)

或者

echo 'hello' | read message

实际上有效。唯一的问题是您使用的 shell 将在子 shell 中运行管道的第二部分。当管道退出时,该子 shell 会被销毁,因此 的值$message不会保留在 shell 中。

在这里你可以看到它的工作原理:

$ echo 'hello' | { read message; echo "$message"; }
hello

...但是由于子 shell 的环境是独立的(并且消失了):

$ echo "$message"

(无输出)

对您来说,一种解决方案是切换到ksh93更智能的解决方案:

$ echo 'hello' | read message
$ echo "$message"
hello

另一个解决方案bash是设置lastpipeshell 选项。这将使管道的最后一部分在当前环境中运行。然而,这在交互式 shell 中不起作用,因为lastpipe要求作业控制未处于活动状态。

#!/bin/bash

shopt -s lastpipe
echo 'hello' | read message
echo "$message"

答案2

使用命令替换:

myvar=`echo foo`

或者

myvar=$(echo foo)

答案3

给定一个修改全局变量并在 stdout 上输出内容的函数:

global_variable=old_value
myfunction() {
  global_variable=new_value
  echo some output
}

ksh93mksh R45 或更新版本中,您可以使用:

var=${
  myfunction
}

然后:

$ print -r -- "$global_variable, $var"
new_value, some output

${ ...; }是一种不会生成子 shell 的命令替换形式。对于内置命令的命令,不要让它们将输出写入管道(为此,您需要不同的进程在管道上读取和写入以避免死锁),ksh93而是让它们不输出任何内容,而是收集它们的内容本来会输出来弥补扩张。mksh使用临时文件代替。

$ ksh -c 'a=${ b=123; echo foo;}; print -r -- "$a $b"'
foo 123

fish的命令替换的行为也类似于 ksh93 的${ ...; }

$ fish -c 'set a (set b 123; echo foo); echo $a $b'
foo 123

在大多数其他 shell 中,您将使用临时文件:

myfunction > file
var=$(cat file) # can be optimised to $(<file) with some shells

在 Linux 上,使用bash4.4 或更早版本或zsh(使用临时文件<<<),您可以执行以下操作:

{
  myfunction > /dev/fd/3 &&
  var=$(cat<&3)
} 3<<< ''

在 中zsh,您还可以执行以下操作:

() {
   myfunction > $1
   var=$(<$1)
} =(:)

在类似 Korn 的 shell 中,例如 ksh、zsh 或 bash,命令替换,无论是$(cmd...)标准形式还是$(<file)${ cmd...; }变体,都会去除所有尾随换行符(来自file或 的输出cmd)。看shell:在命令替换中保留尾随换行符('\n')了解如何解决这个问题。

在 中fishset var (cmd)将 的输出的每一行分配cmd给数组的单独元素$var。无论是输出还是.$var将包含相同的内容。从版本 3.4.0 开始,还支持类似 Korn 的 shell 中的行为(删除所有尾随换行符)。cmdfoofoo<newline>fishset var "$(cmd)"

答案4

我会使用一个临时文件来存储复杂函数的结果,然后从临时文件中读取值进行处理。

# Create a temp file for storage
tmppath=$(mktemp)
# run the command and get the output to the temp file
complex_command > "${tmppath}"
# read the file into variable
message=$(cat "${tmppath}")
# use the variable
echo "$message"

rm -f "${tmppath}"

使用方法mktemp可以参考如何在shell脚本中创建临时文件?

相关内容