Reputation: 1158
with {}
- execute while loop in the current shell
function f {
{
while : ; do echo -n a; done &
} 1>&2
}
a=$( f ); echo "returned"
-> f()
will never return!!!
()
execute while loop in a subshell
function f {
(
while : ; do echo -n a; done &
) 1>&2
}
a=$( f ); echo "returned"
-> f()
will return!!!
Why? will one of them return, but not the other? I don't get it...
The while
loop will be forked and start its own background process due to the ending &
on the while
-loop line. This background process inherits the current open fd list.
As far as I understand, since the while
loop is encapsulated, it inherits the encapsulation's fd list. This is how
{ echo log; echo err 1>&2; } 1>l_file 2>e_file
works as expected, l_file
will contain "log", e_file
will contain "err".
So in either the case of {} 1>&2
or () 1>&2
, bash is informed that it should expect no stdout to capture.
Why is it blocking on the {} 2>&1
case?
{} 1>&- 1>&2
would help?GNU bash, version 4.3.30(1)-release (x86_64-pc-linux-gnu)
Based on the answers so far, I did some more analysis:
11) {}
function f {
{
while : ; do echo -n a; done &
echo "after loop"
} 1>&2
echo "end of function"
}
a=$( f ); echo "returned"
-> after loop
is displayed
12) ()
function f {
(
while : ; do echo -n a; done &
echo "after loop"
) 1>&2
echo "end of function"
}
a=$( f ); echo "returned"
-> after loop
is displayed
-> returned
is displayed
Upvotes: 5
Views: 88
Reputation: 295472
A command substitution doesn't return until the FIFO it opens as output is closed.
When you redirect a subshell, that subshell doesn't hold a file descriptor pointing to the original FD; it doesn't have any need to, since the redirection will be implicitly ended by that subshell's termination.
When you redirect a block, the block needs to retain a copy of the original descriptor to restore on exit. Consequently, there will be an automatically-assigned file descriptor storing a copy of the original (pre-redirection) stdout, and the existence of this FD prevents the FIFO from having the write end closed.
Observe the difference:
f() {
ls -l "/proc/$BASHPID/fd"
}
out1=$( ( f; ) 2>&1; )
out2=$( { f; } 2>&1; )
In the above, out1
may (with irrelevant fields stripped) look like:
0 -> /dev/pts/0
1 -> pipe:[1146313]
2 -> pipe:[1146313]
255 -> /dev/pts/0
...whereas out2
may under similar conditions look like:
0 -> /dev/pts/0
1 -> pipe:[1146327]
10 -> /dev/pts/0
2 -> pipe:[1146327]
255 -> /dev/pts/0
Note the additional FD 10, storing a backup to be restored.
Upvotes: 6