bash command substitution with redirection: bad file descriptor

10,002

Whether redirections are performed before or after assignment expansion is unspecified by POSIX when there's no command, so both are valid and you can't rely on either. So portably, you'd need:

{ out=$(echo "to fd3" >&3; echo "to stdout"); } 3>&1

AT&T ksh and the Bourne shell behave like bash; zsh, pdksh, yash behave like dash in this instance.

Share:
10,002

Related videos on Youtube

Lesmana
Author by

Lesmana

Updated on September 18, 2022

Comments

  • Lesmana
    Lesmana almost 2 years

    the following command works in dash but fails in bash with "Bad file descriptor".

    $ dash -c 'out=$(echo "to fd3" >&3; echo "to stdout") 3>&1; echo "out: $out"'
    to fd3
    out: to stdout
    
    $ bash -c 'out=$(echo "to fd3" >&3; echo "to stdout") 3>&1; echo "out: $out"'
    bash: 3: Bad file descriptor
    out: to stdout
    

    when i replace the command substition with a subshell then it seems to work in dash and bash.

    $ dash -c '(echo "to fd3" >&3; echo "to stdout") 3>&1'
    to fd3
    to stdout
    
    $ bash -c '(echo "to fd3" >&3; echo "to stdout") 3>&1'
    to fd3
    to stdout
    

    versions:

    $ bash --version
    GNU bash, version 4.4.12(1)-release (x86_64-unknown-linux-gnu)
    

    don't know how to get dash version. the man page on my system is dated January 19, 2003.


    research:

    i looked up how bash and dash executes a command. this is what i found.

    for bash: https://www.gnu.org/software/bash/manual/bashref.html#Shell-Operation

    for dash: http://man7.org/linux/man-pages/man1/dash.1.html (section "Simple Commands")

    as far as i understand both do expansions before redirections. command substitution is an expansion. so it makes sense that file descriptor 3 is not set up in the command substitution.

    why does it work in dash? why does it not work in bash? is it a bug in dash? or bash? is it a valid construct at all?

  • The Quark
    The Quark over 2 years
    Why using { } makes it more portable? Does it impose a precedence rule between redirection and assignment expansion (and if so which one) or is there something else to it? { } won't spawn a subshell, right?
  • Stéphane Chazelas
    Stéphane Chazelas over 2 years
    @TheQuark, it did spawn a subshell in the Bourne shell. Not in POSIX / modern shells. Here you're redirecting the command group, so there's not question that redirections within commands inside the command group could be done before.
  • The Quark
    The Quark over 2 years
    Ah, the explanation is lower here: The shell has several programming constructs that are "compound commands", [...]. Each of these compound commands has a reserved word or control operator at the beginning, and a corresponding terminator reserved word or operator at the end. In addition, each can be followed by redirections on the same line as the terminator. Each redirection shall apply to all the commands within the compound command that do not explicitly override that redirection.
  • The Quark
    The Quark over 2 years
    (Just as reference) The key sentence in the unspecified by POSIX when there's no command link is: In the preceding list, the order of steps 3 and 4 may be reversed if no command name results from step 2 or if the command name matches the name of a special built-in utility; see Special Built-In Utilities.