@@ -76,17 +76,48 @@ export function parseCommand(command: string): string[] {
7676 const subshells : string [ ] = [ ]
7777 const quotes : string [ ] = [ ]
7878 const arrayIndexing : string [ ] = [ ]
79+ const arithmeticExpressions : string [ ] = [ ]
80+ const variables : string [ ] = [ ]
81+ const parameterExpansions : string [ ] = [ ]
82+ const processSubstitutions : string [ ] = [ ]
7983
8084 // First handle PowerShell redirections by temporarily replacing them
8185 let processedCommand = command . replace ( / \d * > & \d * / g, ( match ) => {
8286 redirections . push ( match )
8387 return `__REDIR_${ redirections . length - 1 } __`
8488 } )
8589
86- // Handle array indexing expressions: ${array[...]} pattern and partial expressions
87- processedCommand = processedCommand . replace ( / \$ \{ [ ^ } ] * \[ [ ^ \] ] * ( \] ( [ ^ } ] * \} ) ? ) ? / g, ( match ) => {
88- arrayIndexing . push ( match )
89- return `__ARRAY_${ arrayIndexing . length - 1 } __`
90+ // Handle arithmetic expressions: $((...)) pattern
91+ // Match the entire arithmetic expression including nested parentheses
92+ processedCommand = processedCommand . replace ( / \$ \( \( [ ^ ) ] * (?: \) [ ^ ) ] * ) * \) \) / g, ( match ) => {
93+ arithmeticExpressions . push ( match )
94+ return `__ARITH_${ arithmeticExpressions . length - 1 } __`
95+ } )
96+
97+ // Handle parameter expansions: ${...} patterns (including array indexing)
98+ // This covers ${var}, ${var:-default}, ${var:+alt}, ${#var}, ${var%pattern}, etc.
99+ processedCommand = processedCommand . replace ( / \$ \{ [ ^ } ] + \} / g, ( match ) => {
100+ parameterExpansions . push ( match )
101+ return `__PARAM_${ parameterExpansions . length - 1 } __`
102+ } )
103+
104+ // Handle process substitutions: <(...) and >(...)
105+ processedCommand = processedCommand . replace ( / [ < > ] \( [ ^ ) ] + \) / g, ( match ) => {
106+ processSubstitutions . push ( match )
107+ return `__PROCSUB_${ processSubstitutions . length - 1 } __`
108+ } )
109+
110+ // Handle simple variable references: $varname pattern
111+ // This prevents shell-quote from splitting $count into separate tokens
112+ processedCommand = processedCommand . replace ( / \$ [ a - z A - Z _ ] [ a - z A - Z 0 - 9 _ ] * / g, ( match ) => {
113+ variables . push ( match )
114+ return `__VAR_${ variables . length - 1 } __`
115+ } )
116+
117+ // Handle special bash variables: $?, $!, $#, $$, $@, $*, $-, $0-$9
118+ processedCommand = processedCommand . replace ( / \$ [ ? ! # $ @ * \- 0 - 9 ] / g, ( match ) => {
119+ variables . push ( match )
120+ return `__VAR_${ variables . length - 1 } __`
90121 } )
91122
92123 // Then handle subshell commands
@@ -106,7 +137,40 @@ export function parseCommand(command: string): string[] {
106137 return `__QUOTE_${ quotes . length - 1 } __`
107138 } )
108139
109- const tokens = parse ( processedCommand ) as ShellToken [ ]
140+ let tokens : ShellToken [ ]
141+ try {
142+ tokens = parse ( processedCommand ) as ShellToken [ ]
143+ } catch ( error : any ) {
144+ // If shell-quote fails to parse, fall back to simple splitting
145+ console . warn ( "shell-quote parse error:" , error . message , "for command:" , processedCommand )
146+
147+ // Simple fallback: split by common operators
148+ const fallbackCommands = processedCommand
149+ . split ( / (?: & & | \| \| | ; | \| ) / )
150+ . map ( ( cmd ) => cmd . trim ( ) )
151+ . filter ( ( cmd ) => cmd . length > 0 )
152+
153+ // Restore all placeholders for each command
154+ return fallbackCommands . map ( ( cmd ) => {
155+ let result = cmd
156+ // Restore quotes
157+ result = result . replace ( / _ _ Q U O T E _ ( \d + ) _ _ / g, ( _ , i ) => quotes [ parseInt ( i ) ] )
158+ // Restore redirections
159+ result = result . replace ( / _ _ R E D I R _ ( \d + ) _ _ / g, ( _ , i ) => redirections [ parseInt ( i ) ] )
160+ // Restore array indexing expressions
161+ result = result . replace ( / _ _ A R R A Y _ ( \d + ) _ _ / g, ( _ , i ) => arrayIndexing [ parseInt ( i ) ] )
162+ // Restore arithmetic expressions
163+ result = result . replace ( / _ _ A R I T H _ ( \d + ) _ _ / g, ( _ , i ) => arithmeticExpressions [ parseInt ( i ) ] )
164+ // Restore parameter expansions
165+ result = result . replace ( / _ _ P A R A M _ ( \d + ) _ _ / g, ( _ , i ) => parameterExpansions [ parseInt ( i ) ] )
166+ // Restore process substitutions
167+ result = result . replace ( / _ _ P R O C S U B _ ( \d + ) _ _ / g, ( _ , i ) => processSubstitutions [ parseInt ( i ) ] )
168+ // Restore variable references
169+ result = result . replace ( / _ _ V A R _ ( \d + ) _ _ / g, ( _ , i ) => variables [ parseInt ( i ) ] )
170+ return result
171+ } )
172+ }
173+
110174 const commands : string [ ] = [ ]
111175 let currentCommand : string [ ] = [ ]
112176
@@ -151,6 +215,14 @@ export function parseCommand(command: string): string[] {
151215 result = result . replace ( / _ _ R E D I R _ ( \d + ) _ _ / g, ( _ , i ) => redirections [ parseInt ( i ) ] )
152216 // Restore array indexing expressions
153217 result = result . replace ( / _ _ A R R A Y _ ( \d + ) _ _ / g, ( _ , i ) => arrayIndexing [ parseInt ( i ) ] )
218+ // Restore arithmetic expressions
219+ result = result . replace ( / _ _ A R I T H _ ( \d + ) _ _ / g, ( _ , i ) => arithmeticExpressions [ parseInt ( i ) ] )
220+ // Restore parameter expansions
221+ result = result . replace ( / _ _ P A R A M _ ( \d + ) _ _ / g, ( _ , i ) => parameterExpansions [ parseInt ( i ) ] )
222+ // Restore process substitutions
223+ result = result . replace ( / _ _ P R O C S U B _ ( \d + ) _ _ / g, ( _ , i ) => processSubstitutions [ parseInt ( i ) ] )
224+ // Restore variable references
225+ result = result . replace ( / _ _ V A R _ ( \d + ) _ _ / g, ( _ , i ) => variables [ parseInt ( i ) ] )
154226 return result
155227 } )
156228}
0 commit comments