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?
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
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With