Only the exec overwrites a previous file descriptor!
A redirected command sets a file descriptor in a new context:
Code:
exec 5< /etc/nsswitch.conf
read -u 5 passwd1 5< /etc/passwd
read -u 5 nsswitch1
exec 5<&-
echo "$nsswitch1"
echo "$passwd1"
Behind the scene the shell forks; in the forked shell it sets the descriptor then execs the command.
Regarding the redirected code block, it opens a new descriptor context but not a new shell scope (no fork).
Example with nested code blocks:
Code:
exec 5< /etc/nsswitch.conf
{
{
read -u 5 passwd1
} 5< /etc/passwd
read -u 5 group1
} 5< /etc/group
read -u 5 nsswitch1
exec 5<&-
echo "$nsswitch1"
echo "$passwd1"
echo "$group1"
The old Bourne shell forked a sub shell for a redirected code block; shell variables and settings in the sub shell were lost when back in the main shell.
A modern shell avoids a sub shell, unless you enforce it by using ( ) instead of { }
But a pipe from/to a code block still enforces a sub shell.
Code:
echo hello | while read i; do echo "Sub shell: i=$i"; done
echo "Back in the main shell: i=$i"
The while-do-done block is a code block.
BTW even for-do-done if-then-fi case-esac are code blocks. For ex you can do
Code:
if [ "$1" = "head" ]
then
head
else
tail
fi < /etc/group