2

I am using a dynamically assigned file descriptor in a script as described in this post:

zsh and POSIX equivalent of bash's `{var}>&1`

The script itself is meant to be portable between bash and zsh, so emulate -LR bash is executed early on if the script detects it is being run in zsh.

Unfortunately, this is breaking exec {fd}>&..., which has been supported in zsh and bash for quite some time.

To illustrate, this script succeeds (also works in bash):

#!/usr/bin/env zsh

set -euo pipefail

for i in {1..10}; do
  exec {fd}> >( tee /dev/stderr | logger )

  echo "FD: ${fd}" >&"${fd}"
done

But this does not:

#!/usr/bin/env zsh

set -euo pipefail

emulate -LR bash

for i in {1..10}; do
  exec {fd}> >( tee /dev/stderr | logger )

  echo "FD: ${fd}" >&"${fd}"
done

command not found: {fd}

Can anyone point me to a comprehensive list of what emulate -R bash does under the hood (maybe I overlooked it, but I wasn't able to find a real man page for emulate). Or better yet if anyone knows which specific option causes this issue, that would be great.

Thanks in advance for any help.

1 Answer 1

4

zsh has no bash emulation mode. If you pass bash (or anything starting with s or b (for Bourne)) to emulate, it will use the sh emulation mode:

$ emulate bash -c emulate
sh

zsh's emulation modes are zsh (default), sh (initially mostly SysV/Bourne sh, now more POSIX sh), csh and ksh.

bash is a shell that is mostly POSIX sh compliant but with extensions most of which come from the Korn shell, so to have zsh interpret bash code that is using non-POSIX extensions, your best bet is the ksh emulation (you may want to also enable the BASH_REMATCH option intended for bash compatibility).

The {fd}< feature actually comes from zsh. It was implemented at the same time in zsh, ksh93 and bash on a suggestion of one of the zsh developers in a discussion between the maintainers of all 3 shells.

echo {fd}< /dev/null is required by POSIX to output {fd} (though that's going to change in future versions of the standard), so zsh disables it in sh emulation. It doesn't in ksh emulation, so that's the one you'll want to use here.

As for which option is enabled in which emulation (zsh, sh, ksh, csh), see the <C>, <K>, <S>, <Z> next to each option description in the manual (info zsh 'Description of Options').

What option affects the {fd}>... feature is specified in the description of that feature in the manual (info zsh 'file descriptors, use with parameters'): IGNORE_BRACES whose description (info zsh IGNORE_BRACES) has a <S> next to it meaning it's enabled by default in sh emulation only.

Though zsh comes with man pages (a few of them), man pages are rather inadequate for a manual that big. I'd recommend using info instead which is hypertext and has a searchable index and table of contents. To learn about zsh's emulate builtin, you can run info zsh emulate, or from within info zsh, press I for the index, and enter emulate (tab completion is available). On some systems, you may need to install a zsh-doc package or equivalent as info documentation is not always installed by default.

2
  • Great answer, thank you! Not sure where exactly I picked up emulate bash, but serves me right for trusting random blogs. It does seem like it would be a worthwhile zsh feature though as now I will need to piece it together myself.
    – oO.o
    Commented Sep 17, 2020 at 14:52
  • @oO.o, you'll find that a bash emulation mode has been discussed several times on the zsh mailing lists (from as far back as 2001 AFAICS). Commented Sep 17, 2020 at 15:50

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.