То, как «традиционные» оболочки работают с такими вещами, как подстановка переменных, трудно обработать с помощью lex / yacc. То, что они делают, больше похоже на расширение макросов, когда ПОСЛЕ расширения переменной они затем повторно токенизируют ввод, не расширяя другие переменные. Так, например, ввод типа «xx $ {$ foo}», где «foo» определен как «bar», а «bar» определен как «$ y», расширится до «xx $ y», который будет рассматриваться как одно слово (и $ y НЕ будет раскрыто).
Вы можете справиться с этим во флексе, но вам нужно много вспомогательного кода. Вам нужно использовать материал flex в yy_buffer_state, чтобы иногда перенаправлять вывод в буфер, из которого вы затем будете повторно сканировать, и осторожно использовать начальные состояния, чтобы контролировать, когда переменные можно и нельзя расширять.
Возможно, проще использовать очень простой лексер, который возвращает токены, такие как ALPHA (один или несколько буквенных символов), NUMERIC (одна или несколько цифр) или WHITESPACE (один или несколько пробелов или табуляций), и парсер собирает их соответственно, и вы в конечном итоге с правилами, как:
simple_command: wordlist NEWLINE ;
wordlist: word | wordlist WHITESPACE word ;
word: word_frag
| word word_frag { $$ = concat_string($1, $2); }
;
word_frag: single_quote_string
| double_quote_string
| variable
| ALPHA
| NUMERIC
...more options...
;
variable: '$' name { $$ = lookup($2); }
| '$' '{' word '}' { $$ = lookup($3); }
| '$' '{' word ':' ....
Как вы можете видеть, это довольно быстро усложняется.