Skip to content

Single-bracket test commands are merged across redirected statements #316

@wrefactor

Description

@wrefactor

If two single-bracket test commands appear above and below a series of statements containing a redirection, both tests and all statements between will be interpreted as one large redirected_statement inside a single test_command. I believe this is a minimal example:

[ 2 -lt 3 ]
echo >1
[ ]

which is (roughly) parsed as:

test_command(command("2 -lt 3 ]\necho"), file_redirect(">1\n["))
Full parse tree from playground

From the playground, but also seen on tree-sitter-bash 0.25.1 / tree-sitter 0.25.2

program [0, 0] - [4, 0]
  test_command [0, 0] - [2, 3]
    "[" [0, 0] - [0, 1]
    redirected_statement [0, 2] - [2, 1]
      body: command [0, 2] - [1, 4]
        name: command_name [0, 2] - [0, 3]
          number [0, 2] - [0, 3]
        argument: word [0, 4] - [0, 7]
        argument: number [0, 8] - [0, 9]
        argument: word [0, 10] - [0, 11]
        argument: word [1, 0] - [1, 4]
      redirect: file_redirect [1, 5] - [2, 1]
        ">" [1, 5] - [1, 6]
        destination: number [1, 6] - [1, 7]
        destination: word [2, 0] - [2, 1]
    "]" [2, 2] - [2, 3]

The problem still occurs when:

  • adding more statements above/below the echo line (these will all be swallowed up by the redirected_statement)
  • using other operand types (tested numbers "strings", $variables)
  • using other operators (tested ==, -eq, -ne, -lt, -gt, -ge, -le)
  • prefixing with !; adding terms with && or ||; encloding in the ternary operator, e.g. 1 == 2 ? 3 : 4
  • using other redirections (tested >, <, &>, >&', <&, >>`)
  • different commands (instead of echo), different destinations (tested descriptors 0, 1, 2, and names bar)
  • modifying the second test without any of the above restrictions

The problem does not occur when the first test:

  • is empty, a single word, a unary operator (e.g. [ ], [ 0 ], [ -z "" ]), or their negation (e.g. [ ! 0 ], or [ ! -z "" ])
  • contains a parenthesised sub-expression
  • contains any symbolic (i.e. not -[a-z][a-z]) binary operator, except ==, && or ||
  • contains a parenthesised sub-expression
    or when the > redirection is replaced with a heredoc/herestring (<<< or <<)

Hope that's useful, and thanks again!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions