我试图让bash处理来自stdin的数据,但没有运气。我的意思是以下工作都不做:

echo "hello world" | test=($(< /dev/stdin)); echo test=$test
test=

echo "hello world" | read test; echo test=$test
test=

echo "hello world" | test=`cat`; echo test=$test
test=

我希望输出为test=hello world。我尝试在“$test”周围加上“”引号,但也不起作用。


当前回答

从shell命令到bash变量的隐式管道的语法为

var=$(command)

or

var=`command`

在示例中,将数据输送到赋值语句,该语句不需要任何输入。

其他回答

从shell命令到bash变量的隐式管道的语法为

var=$(command)

or

var=`command`

在示例中,将数据输送到赋值语句,该语句不需要任何输入。

第一次尝试非常接近。这种变化应该是有效的:

echo "hello world" | { test=$(< /dev/stdin); echo "test=$test"; };

输出为:

test = hello world

在管道后面需要用大括号括起要测试的赋值和回显。

如果没有大括号,test的赋值(在管道之后)在一个shell中,而echo "test=$test"在一个单独的shell中,该shell不知道该赋值。这就是为什么在输出中得到"test="而不是"test=hello world"。

问题是如何捕获命令的输出,以保存在变量中,以便稍后在脚本中使用。我可能会重复一些之前的答案,但我试着把我能想到的所有答案排列起来进行比较和评论,所以请容忍我。

直观的构造

echo test | read x
echo x=$x

在Korn shell中是有效的,因为ksh已经实现了管道系列中的最后一个命令是当前shell的一部分。前面的管道命令是子shell。相反,其他shell将所有管道命令定义为子shell,包括最后一个。 这正是我更喜欢ksh的原因。 但必须复制与其他shell, bash f.ex。,则必须使用另一种结构。

要捕获1个值,这个构造是可行的:

x=$(echo test)
echo x=$x

但这只能满足收集1个值以供以后使用的需求。

为了捕获更多的值,这个构造是有用的,在bash和ksh中工作:

read x y <<< $(echo test again)
echo x=$x y=$y

有一个变种,我注意到在bash工作,但不是在ksh:

read x y < <(echo test again)
echo x=$x y=$y

<<< $(…)是一个here-document变体,它提供了标准命令行的所有元处理。< <(…)是文件替换操作符的输入重定向。

我现在在所有脚本中都使用“<<< $(”,因为它似乎是shell变量之间最可移植的构造。我有一套工具,可以在任何Unix风格的作业中随身携带。

当然,有一个普遍可行但粗糙的解决方案:

command-1 | {command-2; echo "x=test; y=again" > file.tmp; chmod 700 file.tmp}
. ./file.tmp
rm file.tmp
echo x=$x y=$y

因为我上当了,我想留个便条。 我发现了这个线程,因为我必须重写一个旧的sh脚本 POSIX兼容。 这基本上意味着绕过POSIX引入的管道/子shell问题,重写如下代码:

some_command | read a b c

成:

read a b c << EOF
$(some_command)
EOF

代码是这样的:

some_command |
while read a b c; do
    # something
done

成:

while read a b c; do
    # something
done << EOF
$(some_command)
EOF

但后者在空输入时表现不一样。 使用旧的符号,while循环不会在空输入时输入, 但在POSIX符号中它是! 我认为这是由于EOF之前的换行, 这一点不容忽视。 POSIX代码的行为更像旧的符号 看起来是这样的:

while read a b c; do
    case $a in ("") break; esac
    # something
done << EOF
$(some_command)
EOF

在大多数情况下,这就足够了。 但不幸的是,这仍然不完全像以前的符号 如果some_command打印空行。 在旧的表示法中,执行while主体 在POSIX符号中,我们在主体前面中断。

解决这个问题的方法是这样的:

while read a b c; do
    case $a in ("something_guaranteed_not_to_be_printed_by_some_command") break; esac
    # something
done << EOF
$(some_command)
echo "something_guaranteed_not_to_be_printed_by_some_command"
EOF

Bash 4.2引入了lastpipe选项,通过在当前shell(而不是子shell)中的管道中执行最后一条命令,允许代码按编写的方式工作。

shopt -s lastpipe
echo "hello world" | read test; echo test=$test