在bash中,管道未设置值后读取


22

编辑:原始标题为“ bash读取失败”

使用ksh时,我将read用作分隔值的便捷方法:

$ echo 1 2 3 4 5 | read a b dump
$ echo $b $a 
2 1
$

但是它在bash中失败了:

$ echo 1 2 3 4 5 | read a b dump
$ echo $b $a 

$

我没有在手册页中找到失败的原因,知道吗?


2
这在Greg的Bash FAQ的第024页中进行了讨论(有些晦涩)。
斯科特

Answers:


27

bash 子外壳程序上下文中运行管道的右侧,因此read不会保留对变量的更改(变量所做的更改),这些更改将在命令末尾在子外壳程序执行时死亡。

相反,您可以使用流程替换

$ read a b dump < <(echo 1 2 3 4 5)
$ echo $b $a
2 1

在这种情况下,read它在我们的主shell中运行,而我们的output-production命令在子shell中运行。该<(...)语法创建了一个子外壳,并将其输出连接到管道,我们read通过普通<操作将其重定向到输入。因为read在我们的主外壳中运行,所以变量设置正确。

如评论中所指出,如果您的目标是从字面上将字符串以某种方式拆分为变量,则可以使用here字符串

read a b dump <<<"1 2 3 4 5"

我认为还有更多,但如果没有,这是一个更好的选择。


3
甚至read a b dump <<< '1 2 3 4 5'
choroba 2014年

谢谢大家,顺便说一句,我注意到mksh(在cygwin上)的作用与bash相同。
伊曼纽尔2014年

@Michael Homer好的解释!我找到了另一个示例,可以解释管道中的每个命令都在自己的子Shell中运行cat /etc/passwd | (read -r line ; echo $line)。但接下来echo$line这是不是在屏幕上的管道放什么,因为价值只是括号之间(子shell)存在。希望,它可以帮助某人。
尤里·贡恰鲁克

17

这不是一个bash问题,问题POSIX同时允许bashksh行为,从而导致不幸的差异所观察。

http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_12

另外,多命令管道中的每个命令都在子shell环境中。但是,作为扩展,流水线中的任何或所有命令都可以在当前环境中执行。所有其他命令应在当前的外壳环境中执行。

但是,对于bash 4.2更新的版本,您可以lastpipe在非交互式脚本中设置该选项以获得预期的结果,例如:

#!/bin/bash

echo 1 2 3 4 5 | read a b dump
echo before: $b $a 
shopt -s lastpipe
echo 1 2 3 4 5 | read a b dump
echo after: $b $a 

输出:

before:
after: 2 1

1
+1感谢您提供“ lastpipe”信息。抱歉,延迟
Emmanuel

1
问题lastpipe在于它不能在其他shell中工作(例如破折号)。除了运行该子外壳中的所有内容外,基本上没有任何方法可以进行此便携式移植,请参见stackoverflow.com/questions/36268479/…–
anarcat

1
@anarcat这是正确的,但是这里的问题是关于bash的。
jlliagre

@anarcat:将来可能会改变,因为希望出于其他原因更改POSIX(PIPE状态),请参见此处:unix.stackexchange.com/questions/476834/…更改其他shell并非易事,我花了几个月的时间重写Bourne Shell(bosh)上的解析器和插入器,以实现现代ksh的更快行为。
schily
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.