So in other words, you're saying I should use '... | eval "exec $x>&-; llfd"' instead of '... | eval "llfd $x>&-"'. This way the subshell won't be assuming I might use $x later. That works, but I still find it counterintuitive that with the original syntax the subshell doesn't realize there's nothing left to execute after $x>&-.
Also, I tried your other suggestions. The second one 'llfd () { bash -c .... }' works, but the other 'llfd () ( ... )' doesn't! I tried to understand why... Looking at this: $ bash -c 'llfd () { echo "pid:$BASHPID" >&2; ls -gG /proc/$BASHPID/fd >&2; }; f () { llfd; (llfd); bash -c "echo pid:\$\$ >&2; ls -gG /proc/\$\$/fd >&2"; }; x=3; exec 4>/tmp/fd_4; coproc cat; eval "exec $x>/tmp/fd_3"; llfd; echo | eval "f $x>&-"' pid:14920 total 0 lrwx------ 1 64 Feb 12 14:03 0 -> /dev/pts/2 lrwx------ 1 64 Feb 12 14:03 1 -> /dev/pts/2 lrwx------ 1 64 Feb 12 14:03 2 -> /dev/pts/2 l-wx------ 1 64 Feb 12 14:03 3 -> /tmp/fd_3 l-wx------ 1 64 Feb 12 14:03 4 -> /tmp/fd_4 l-wx------ 1 64 Feb 12 14:03 60 -> pipe:[5010928] lr-x------ 1 64 Feb 12 14:03 63 -> pipe:[5010927] lr-x------ 1 64 Feb 12 14:03 8 -> /proc/4520/auxv pid:14924 total 0 lr-x------ 1 64 Feb 12 14:03 0 -> pipe:[5007145] lrwx------ 1 64 Feb 12 14:03 1 -> /dev/pts/2 l-wx------ 1 64 Feb 12 14:03 10 -> /tmp/fd_3 lrwx------ 1 64 Feb 12 14:03 2 -> /dev/pts/2 l-wx------ 1 64 Feb 12 14:03 4 -> /tmp/fd_4 lr-x------ 1 64 Feb 12 14:03 8 -> /proc/4520/auxv pid:14926 total 0 lr-x------ 1 64 Feb 12 14:03 0 -> pipe:[5007145] lrwx------ 1 64 Feb 12 14:03 1 -> /dev/pts/2 l-wx------ 1 64 Feb 12 14:03 10 -> /tmp/fd_3 lrwx------ 1 64 Feb 12 14:03 2 -> /dev/pts/2 l-wx------ 1 64 Feb 12 14:03 4 -> /tmp/fd_4 lr-x------ 1 64 Feb 12 14:03 8 -> /proc/4520/auxv pid:14928 total 0 lr-x------ 1 64 Feb 12 14:03 0 -> pipe:[5007145] lrwx------ 1 64 Feb 12 14:03 1 -> /dev/pts/2 lrwx------ 1 64 Feb 12 14:03 2 -> /dev/pts/2 l-wx------ 1 64 Feb 12 14:03 4 -> /tmp/fd_4 lr-x------ 1 64 Feb 12 14:03 8 -> /proc/4520/auxv ... there seem to be not 2 but 3(!) types of file descriptors: 1. fds which are copied across both subshells and exec; like 4 2. fds which are not copied across subshells; like 60&63 3. fds which are copied across subshells, but not exec; like 10 I knew about types 1&2, but not about type 3. Apparently with your first suggestion, fd 10 is created and survives a subshell creation. Is this correct?? On Tue, Feb 12, 2013 at 11:40 AM, Pierre Gaston <pierre.gas...@gmail.com>wrote: > > > On Tue, Feb 12, 2013 at 6:07 PM, Matei David <matei.da...@gmail.com>wrote: > >> Ok, but I see the same behaviour when eval runs in a subshell: >> >> $ bash -c 'llfd () { echo "pid:$BASHPID" >&2; ls -l /proc/$BASHPID/fd/ >> >&2; }; x=3; eval "exec $x>/dev/null"; llfd; echo | eval "llfd $x>&-"' >> [same output, fd 10 open, pointing to /dev/null, even though it's a >> subshell] >> > > eval runs in a subshell, but it's the same thing inside this subshell. > eg you could have: echo | { eval "llfd "$x>&-"; echo blah >&3; } > > Bash could optimize this once it realizes there's only one command, but > it's probably not that simple to implement. > > Try with a function that spawns a subshell eg: > llfd () ( echo "pid:$BASHPID" >&2; ls -l /proc/$BASHPID/fd/ >&2; ) > > or llfd () { bash -c 'ls -l /proc/$$/fd' ; } > > >