Skip to content

typeset/declare parser silently drops scalar variables whose value starts with '[' or is an empty string #720

Description

@truongsontung

Description

The typeset/declare parser (added in v1.25.7) silently drops any scalar variable whose value starts with [ or whose value is an empty string. The variable simply does not appear in the output — no error, no warning.

The root cause is the value sub-pattern in VAR_DEF_PATTERN:

https://github.com/kellyjonbrazil/jc/blob/master/jc/parsers/typeset.py#L136

VAR_DEF_PATTERN = re.compile(r'(?P<name>[a-zA-Z_][a-zA-Z0-9_]*)=(?P<val>[^(][^[].+)$')

In declare -p output the value is wrapped in quotes, so the value group sees "...". The three parts [^(] [^[] .+ mean:

  • [^(] matches the opening quote " (this correctly excludes array bodies that start with (),
  • [^[] then matches the first character of the actual value and forbids it from being [,
  • .+ forces the remaining value to be at least one more character (so "" — an empty string — cannot match either).

So a legitimate scalar whose value begins with [, or an empty string, fails VAR_DEF_PATTERN, matches none of the other patterns (they all require the value to start with (), and is dropped.

Steps to Reproduce

import jc.parsers.typeset as t

# value starts with '['  -> dropped
print(t.parse('declare -- a="[x]"', quiet=True))          # []

# empty string value     -> dropped
print(t.parse('declare -- e=""', quiet=True))             # []

# control: normal value  -> works
print(t.parse('declare -- ok="hello"', quiet=True))       # [{... 'value': 'hello' ...}]

Silent data loss in a realistic multi-variable dump:

data = '''declare -- var1="hello"
declare -- var2="[bracketed]"
declare -- var3="world"'''
out = t.parse(data, quiet=True)
print(len(out))   # 2  (expected 3 — var2 is silently missing)

These are all valid bash values, e.g.:

$ mystr="[test]"; declare -p mystr
declare -- mystr="[test]"

Expected Behavior

All three variables should be parsed; var2 should appear with "value": "[bracketed]", and empty-string variables should appear with "value": "".

Actual Behavior

var2 (and empty-string variables) are silently omitted from the output.

Suggested Fix

The [^[] second-character restriction and the .+ minimum-length requirement are unnecessary — the only thing needed to distinguish a scalar from an array is that the value does not start with (. Changing the value group to [^(].* fixes both cases while still routing array definitions to the array patterns:

VAR_DEF_PATTERN = re.compile(r'(?P<name>[a-zA-Z_][a-zA-Z0-9_]*)=(?P<val>[^(].*)$')

Verified against the affected inputs:

input [^(].* result
declare -- a="[x]" name=a, val="[x]"
declare -- e="" name=e, val=""
declare -- ok="(paren)" name=ok, val="(paren)" ✓ (still scalar)
declare -a arr=([0]="x") no match ✓ (falls through to array pattern)

Environment

  • jc version: 1.25.7 (current master, verified via git clone --depth 1)
  • Python: 3.12

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions