2012-08-28 47 views
6

我想在一个循环中创建bash别名,循环是从命令读取行。为了逐行读取输出,我相信我需要将输出传送到read。但是,当我这样做时,别名就不存在了。为什么我不能在一个while循环中进行评估?

如果我包括我的.bashrc如下:

for x in a1 a2; do 
    eval "alias $x='echo foo'" 
done 

echo -e "a3\na4" | while read x; do 
    eval "alias $x='echo foo'" 
done 

别名a1a2存在,但a3a4没有。这两个循环之间有什么区别?

+0

这是管道到'while'循环时很常见的问题。见[这个答案](http://stackoverflow.com/questions/7612320/bash-weird-variable-scope-when-populating-array-with-results/7612420#7612420),或[BashFAQ/024](http: //mywiki.wooledge.org/BashFAQ/024)了解详情和各种替代解决方案。 –

+5

另外,没有必要在这里使用'eval'。 '别名$ x ='echo foo''将在定义别名之前展开'$ x'。 – chepner

回答

5

问题在于管线。在形式为a | b | c的流水线中,每个单独的命令a,bc在单独的子shell中运行,这意味着它接收到父母的执行环境(包括别名)的副本,并且任何改变它制作自己的副本(例如通过运行alias)将不会影响父母[ ref]。

在你的情况,你可以通过书面形式解决这个问题:

while read x; do 
    eval "alias $x='echo foo'" 
done < <(echo -e "a3\na4") 

仍然将运行在一个子shell echo -e "a3\na4",但将运行在正常/父执行环境while -loop。

+0

我以前没见过'<(command)'语法。是创建一个指向'command'输出的文件描述符吗? –

+0

@JeffKlukas:是的;它被称为“流程替换”(请参阅​​http://www.gnu.org/software/bash/manual/bashref.html#Process-Substitution),它使用FIFO或'/ dev/fd'文件。文档暗示它不适用于所有系统,但它适用于我尝试过的所有系统,包括Cygwin。 – ruakh

6

管道中的while循环运行在子shell中。你可以这样做:

while read x; do 
    eval "alias $x='echo foo'" 
done << EOF 
a3 
a4 
EOF 
3

在bash 4.2和更高版本,您可以设置lastpipe选项,以防止管道的最后命令在一个子shell执行。工作控制也必须关闭(set +m)才能使其工作。

shopt -s lastpipe 
set +m 
echo -e "a3\na4" | while read x; do 
    alias $x='echo foo' 
done 
+0

+1:很高兴知道! – ruakh

相关问题