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

Par*_*art 45 bash pipe stdin

我正在尝试将管道的输出转换为变量。我尝试了以下几件事:

echo foo | myvar=$(</dev/stdin)
echo foo | myvar=$(cat)
echo foo | myvar=$(tee)
Run Code Online (Sandbox Code Playgroud)

但是$myvar是空的。

我不想做:

myvar=$(echo foo)
Run Code Online (Sandbox Code Playgroud)

因为我不想生成子shell。

有任何想法吗?

编辑:我不想生成子shell,因为管道之前的命令需要编辑全局变量,而这在子shell中是做不到的。它可以?的echo东西只是为了简化。它更像是:

complex_function | myvar=$(</dev/stdin)
Run Code Online (Sandbox Code Playgroud)

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

complex_function | echo $(</dev/stdin)
Run Code Online (Sandbox Code Playgroud)

Kus*_*nda 55

正确的解决方案是使用这样的命令替换:

variable=$(complex_command)
Run Code Online (Sandbox Code Playgroud)

message=$(echo 'hello')
Run Code Online (Sandbox Code Playgroud)

(或就此而言,message=hello在这种情况下)。

您的管道:

echo 'hello' | message=$(</dev/stdin)
Run Code Online (Sandbox Code Playgroud)

或者

echo 'hello' | read message
Run Code Online (Sandbox Code Playgroud)

实际上有效。唯一的问题是您使用的外壳程序将在子外壳程序中运行管道的第二部分。这个子shell在管道退出时被销毁,所以$messageshell中不保留的值。

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

$ echo 'hello' | { read message; echo "$message"; }
hello
Run Code Online (Sandbox Code Playgroud)

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

$ echo "$message"
Run Code Online (Sandbox Code Playgroud)

(无输出)

您的一种解决方案是切换到ksh93更聪明的方法:

$ echo 'hello' | read message
$ echo "$message"
hello
Run Code Online (Sandbox Code Playgroud)

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

#!/bin/bash

shopt -s lastpipe
echo 'hello' | read message
echo "$message"
Run Code Online (Sandbox Code Playgroud)


Zum*_*rio 9

使用命令替换:

myvar=`echo foo`
Run Code Online (Sandbox Code Playgroud)

或者

myvar=$(echo foo)
Run Code Online (Sandbox Code Playgroud)

  • `\`echo foo\`` 只是`$(echo foo)` 的弃用替代品,不是吗? (4认同)
  • OP实际上说“我不想那样做......” (4认同)
  • @帕克沃特是的。好吧,实际上并没有“弃用”,它仍然受到支持。只是`$()` 几乎总是更好。 (2认同)
  • 命令替换仍然会产生一个子shell,所以它在这里对OP没有帮助。 (2认同)

Sté*_*las 6

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

global_variable=old_value
myfunction() {
  global_variable=new_value
  echo some output
}
Run Code Online (Sandbox Code Playgroud)

ksh93在mksh R45 或更新版本中,您可以使用:

var=${
  myfunction
}
Run Code Online (Sandbox Code Playgroud)

然后:

$ print -r -- "$global_variable, $var"
new_value, some output
Run Code Online (Sandbox Code Playgroud)

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

$ ksh -c 'a=${ b=123; echo foo;}; print -r -- "$a $b"'
foo 123
Run Code Online (Sandbox Code Playgroud)

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

$ fish -c 'set a (set b 123; echo foo); echo $a $b'
foo 123
Run Code Online (Sandbox Code Playgroud)

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

myfunction > file
var=$(cat file) # can be optimised to $(<file) with some shells
Run Code Online (Sandbox Code Playgroud)

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

{
  myfunction > /dev/fd/3 &&
  var=$(cat<&3)
} 3<<< ''
Run Code Online (Sandbox Code Playgroud)

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

() {
   myfunction > $1
   var=$(<$1)
} =(:)
Run Code Online (Sandbox Code Playgroud)

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

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


归档时间:

查看次数:

120828 次

最近记录:

4 年,6 月 前