Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can't I eval inside a while loop?

Tags:

bash

shell

I want to create bash aliases within a loop where the loop is reading lines from a command. In order to read output line-by-line, I believe I need to pipe the output into read. When I do that, however, the aliases don't exist.

If I include the following in my .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

Aliases a1 and a2 exist, but a3 and a4 do not. What is the difference between those two loops?

like image 506
Jeff Klukas Avatar asked Aug 28 '12 17:08

Jeff Klukas


3 Answers

The while loop in the pipe runs in a subshell. You can do:

while read x; do
    eval "alias $x='echo foo'"
done << EOF
a3
a4 
EOF
like image 122
William Pursell Avatar answered Nov 17 '22 17:11

William Pursell


The problem is the pipeline. In a pipeline of the form a | b | c, each of the individual commands a, b, and c is run in a separate subshell [ref], which means that it receives a copy of the parent's execution environment (including aliases), and any changes it makes to its own copy (such as by running alias) will have no effect on the parent [ref].

In your case, you could fix this by writing:

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

which will still run echo -e "a3\na4" in a subshell, but will run the while-loop in the normal/parent execution environment.

like image 5
ruakh Avatar answered Nov 17 '22 16:11

ruakh


In bash 4.2 and later, you can set the lastpipe option to keep the last command of a pipe from executing in a subshell. Job control must also be off (set +m) for this to work.

shopt -s lastpipe
set +m
echo -e "a3\na4" | while read x; do
    alias $x='echo foo'
done
like image 3
chepner Avatar answered Nov 17 '22 15:11

chepner