10

我需要在 bash 脚本中将一些配置数据读入环境变量中。

“明显”(但不正确)的模式是:

egrep "pattern" config-file.cfg | read VAR1 VAR2 VAR3 etc...

这会失败,因为read它在子 shell 中运行,因此无法在调用 shell 中设置变量。所以我想出了这个作为替代方案

coproc egrep "pattern" config-file.cfg
read -u ${COPROC[0]} VAR1 VAR2 VAR3 etc...

效果很好。

为了测试如果协同进程返回多行会发生什么,我尝试了这个:

coproc cat config-file.cfg
read -u ${COPROC[0]} VAR1 VAR2 VAR3 etc...

其中config-file.cfg包含三行。

$ cat config-file.cfg
LINE1 A1 B1 C1
LINE2 A2 B2 C2
LINE3 A3 B3 C3

我希望这会处理文件中的第一行,然后是某种“断管”错误消息。虽然它确实处理了第一行,但没有错误消息,也没有协进程在运行。

所以我然后在脚本中尝试了以下内容:

$ cat test.sh
coproc cat config-file.cfg
read -u ${COPROC[0]} VAR1 VAR2 VAR3 VAR4
echo $VAR1 $VAR2 $VAR3 $VAR4
wait
echo $?

运行它:

$ bash -x test.sh
+ read -u 63 VAR1 VAR2 VAR3 VAR4
+ cat config-file.cfg
LINE1 A1 B1 C1
+ wait
+ echo 0
0

剩下的两条线去哪儿了?我本来希望“破管”或wait挂起,因为没有什么可以读取剩余的行,但正如您所见,返回码为零。

4

3 回答 3

6

根据上面的评论,您可以使用流程替换来实现这一点。这样,read不会在子 shell 中运行,并且捕获的变量将在当前 shell 中可用。

read VAR1 VAR2 VAR3 < <(egrep "pattern" config-file.cfg)

“如果使用 <(list) 形式,则应读取作为参数传递的文件以获得列表的输出” - 他们在谈论什么“作为参数传递的文件”?

这对我来说也很神秘。Advanced Bash-scripting Guide中关于进程替换的章节有更全面的解释。

我看到它的方式,当使用<(cmd)语法时,输出cmd是通过命名管道(或临时文件)提供的,并且语法被管道/文件的文件名替换。因此,对于上面的示例,它最终将等同于:

read VAR1 VAR2 VAR3 < /dev/fd/63

/dev/fd/63连接到标准输出的命名管道在哪里cmd

于 2011-10-04T20:20:57.050 回答
2

如果我正确理解了您的问题(并且我希望我没有说明显而易见的问题), 请一次读取一行,如下所示:

$ read a b c < config-file.cfg && echo $?
0

或者:

$ printf '%s\n%s\n' one two | { read; echo "$REPLY";}
one

$ echo ${PIPESTATUS[@]}
0 0

要读取所有输入,您需要一个循环:

$ coproc cat config-file.cfg
[1] 3460

$ while read -u ${COPROC[0]} VAR1 VAR2 VAR3; do echo $VAR1 $VAR2 $VAR3; done
LINE1 A1 B1 C1
LINE2 A2 B2 C2
LINE3 A3 B3 C3
[1]+  Done                    coproc COPROC cat config-file.cfg

只是补充一点,这在常见问题解答中进行了解释。

于 2011-10-04T18:41:21.187 回答
1

发生的情况是,一旦子 shell 完成,父 shell 就会清理并关闭 FD。你很幸运,你甚至可以阅读第一行!

在交互式 shell 中试试这个:

$ coproc ECHO { echo foo; echo bar; }
[2] 16472
[2]+  Done                    coproc ECHO { echo foo; echo bar; }
$ read -u ${ECHO[0]}; echo $REPLY
bash: read: -u: option requires an argument
read: usage: read [-ers] [-a array] [-d delim] [-i text] [-n nchars] [-N nchars] [-p prompt] [-t timeout] [-u fd] [name ...]

它甚至清除了环境变量。

现在试试这个:

$ coproc ECHO { echo foo; echo bar; sleep 30; }
[2] 16485
$ read -u ${ECHO[0]}; echo $REPLY
foo
$ read -u ${ECHO[0]}; echo $REPLY
bar
$ read -u ${ECHO[0]}; echo $REPLY # blocks until the 30 seconds are up

[2]+  Done                    coproc ECHO { echo foo; echo bar; sleep 30; }

至于解决问题背后的问题:是的,对于给出的特定示例,重定向和进程替换是更好的选择。

于 2019-03-09T12:59:02.243 回答