我需要在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
挂起,因为没有什么东西可以读取其余的行,但正如你所看到的那样,返回代码为零。
答案 0 :(得分:6)
根据上述评论,您可以使用process substitution来实现这一目标。这样,read
不会在子shell中运行,捕获的变量将在当前shell中可用。
read VAR1 VAR2 VAR3 < <(egrep "pattern" config-file.cfg)
“如果使用&lt;(list)表单,则应该读取作为参数传递的文件以获取列表的输出” - 作为agrument传递的“什么”文件“他们在谈论什么?
这对我来说也很神秘。高级Bash脚本指南中的chapter on process substitution有更全面的解释。
我看到它的方式,当使用<(cmd)
语法时,cmd
的输出通过命名管道(或临时文件)提供,语法被替换为文件名管/文件。因此,对于上面的示例,它最终将等同于:
read VAR1 VAR2 VAR3 < /dev/fd/63
其中/dev/fd/63
是连接到cmd
标准输出的命名管道。
答案 1 :(得分:2)
如果我理解你的问题(我希望我没说明显的话), read 一次读取一行,如:
$ 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
只需补充说明FAQ。
中对此进行了解释答案 2 :(得分:1)
发生的情况是,一旦子外壳程序完成,父外壳程序便会清理并关闭FD。您很幸运,您甚至必须阅读第一行!
在交互式外壳中尝试:
$ 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; }
对于解决问题背后的问题:是的,对于给定的特定示例,重定向和流程替换是更好的选择。