@@ -311,6 +311,109 @@ impl<'a> Parser<'a> {
311311 }
312312 }
313313
314+ /// Parse redirections that follow a compound command (>, >>, 2>, etc.)
315+ fn parse_trailing_redirects ( & mut self ) -> Vec < Redirect > {
316+ let mut redirects = Vec :: new ( ) ;
317+ loop {
318+ match & self . current_token {
319+ Some ( tokens:: Token :: RedirectOut ) => {
320+ self . advance ( ) ;
321+ if let Ok ( target) = self . expect_word ( ) {
322+ redirects. push ( Redirect {
323+ fd : None ,
324+ kind : RedirectKind :: Output ,
325+ target,
326+ } ) ;
327+ }
328+ }
329+ Some ( tokens:: Token :: RedirectAppend ) => {
330+ self . advance ( ) ;
331+ if let Ok ( target) = self . expect_word ( ) {
332+ redirects. push ( Redirect {
333+ fd : None ,
334+ kind : RedirectKind :: Append ,
335+ target,
336+ } ) ;
337+ }
338+ }
339+ Some ( tokens:: Token :: RedirectIn ) => {
340+ self . advance ( ) ;
341+ if let Ok ( target) = self . expect_word ( ) {
342+ redirects. push ( Redirect {
343+ fd : None ,
344+ kind : RedirectKind :: Input ,
345+ target,
346+ } ) ;
347+ }
348+ }
349+ Some ( tokens:: Token :: RedirectBoth ) => {
350+ self . advance ( ) ;
351+ if let Ok ( target) = self . expect_word ( ) {
352+ redirects. push ( Redirect {
353+ fd : None ,
354+ kind : RedirectKind :: OutputBoth ,
355+ target,
356+ } ) ;
357+ }
358+ }
359+ Some ( tokens:: Token :: DupOutput ) => {
360+ self . advance ( ) ;
361+ if let Ok ( target) = self . expect_word ( ) {
362+ redirects. push ( Redirect {
363+ fd : Some ( 1 ) ,
364+ kind : RedirectKind :: DupOutput ,
365+ target,
366+ } ) ;
367+ }
368+ }
369+ Some ( tokens:: Token :: RedirectFd ( fd) ) => {
370+ let fd = * fd;
371+ self . advance ( ) ;
372+ if let Ok ( target) = self . expect_word ( ) {
373+ redirects. push ( Redirect {
374+ fd : Some ( fd) ,
375+ kind : RedirectKind :: Output ,
376+ target,
377+ } ) ;
378+ }
379+ }
380+ Some ( tokens:: Token :: RedirectFdAppend ( fd) ) => {
381+ let fd = * fd;
382+ self . advance ( ) ;
383+ if let Ok ( target) = self . expect_word ( ) {
384+ redirects. push ( Redirect {
385+ fd : Some ( fd) ,
386+ kind : RedirectKind :: Append ,
387+ target,
388+ } ) ;
389+ }
390+ }
391+ Some ( tokens:: Token :: DupFd ( src_fd, dst_fd) ) => {
392+ let src_fd = * src_fd;
393+ let dst_fd = * dst_fd;
394+ self . advance ( ) ;
395+ redirects. push ( Redirect {
396+ fd : Some ( src_fd) ,
397+ kind : RedirectKind :: DupOutput ,
398+ target : Word :: literal ( dst_fd. to_string ( ) ) ,
399+ } ) ;
400+ }
401+ _ => break ,
402+ }
403+ }
404+ redirects
405+ }
406+
407+ /// Parse a compound command and any trailing redirections
408+ fn parse_compound_with_redirects (
409+ & mut self ,
410+ parser : impl FnOnce ( & mut Self ) -> Result < CompoundCommand > ,
411+ ) -> Result < Option < Command > > {
412+ let compound = parser ( self ) ?;
413+ let redirects = self . parse_trailing_redirects ( ) ;
414+ Ok ( Some ( Command :: Compound ( compound, redirects) ) )
415+ }
416+
314417 /// Parse a single command (simple or compound)
315418 fn parse_command ( & mut self ) -> Result < Option < Command > > {
316419 self . skip_newlines ( ) ?;
@@ -319,12 +422,12 @@ impl<'a> Parser<'a> {
319422 if let Some ( tokens:: Token :: Word ( w) ) = & self . current_token {
320423 let word = w. clone ( ) ;
321424 match word. as_str ( ) {
322- "if" => return self . parse_if ( ) . map ( |c| Some ( Command :: Compound ( c ) ) ) ,
323- "for" => return self . parse_for ( ) . map ( |c| Some ( Command :: Compound ( c ) ) ) ,
324- "while" => return self . parse_while ( ) . map ( |c| Some ( Command :: Compound ( c ) ) ) ,
325- "until" => return self . parse_until ( ) . map ( |c| Some ( Command :: Compound ( c ) ) ) ,
326- "case" => return self . parse_case ( ) . map ( |c| Some ( Command :: Compound ( c ) ) ) ,
327- "time" => return self . parse_time ( ) . map ( |c| Some ( Command :: Compound ( c ) ) ) ,
425+ "if" => return self . parse_compound_with_redirects ( |s| s . parse_if ( ) ) ,
426+ "for" => return self . parse_compound_with_redirects ( |s| s . parse_for ( ) ) ,
427+ "while" => return self . parse_compound_with_redirects ( |s| s . parse_while ( ) ) ,
428+ "until" => return self . parse_compound_with_redirects ( |s| s . parse_until ( ) ) ,
429+ "case" => return self . parse_compound_with_redirects ( |s| s . parse_case ( ) ) ,
430+ "time" => return self . parse_compound_with_redirects ( |s| s . parse_time ( ) ) ,
328431 "function" => return self . parse_function_keyword ( ) . map ( Some ) ,
329432 _ => {
330433 // Check for POSIX-style function: name() { body }
@@ -340,19 +443,17 @@ impl<'a> Parser<'a> {
340443
341444 // Check for arithmetic command ((expression))
342445 if matches ! ( self . current_token, Some ( tokens:: Token :: DoubleLeftParen ) ) {
343- return self
344- . parse_arithmetic_command ( )
345- . map ( |c| Some ( Command :: Compound ( c) ) ) ;
446+ return self . parse_compound_with_redirects ( |s| s. parse_arithmetic_command ( ) ) ;
346447 }
347448
348449 // Check for subshell
349450 if matches ! ( self . current_token, Some ( tokens:: Token :: LeftParen ) ) {
350- return self . parse_subshell ( ) . map ( |c| Some ( Command :: Compound ( c ) ) ) ;
451+ return self . parse_compound_with_redirects ( |s| s . parse_subshell ( ) ) ;
351452 }
352453
353454 // Check for brace group
354455 if matches ! ( self . current_token, Some ( tokens:: Token :: LeftBrace ) ) {
355- return self . parse_brace_group ( ) . map ( |c| Some ( Command :: Compound ( c ) ) ) ;
456+ return self . parse_compound_with_redirects ( |s| s . parse_brace_group ( ) ) ;
356457 }
357458
358459 // Default to simple command
@@ -1009,7 +1110,7 @@ impl<'a> Parser<'a> {
10091110
10101111 Ok ( Command :: Function ( FunctionDef {
10111112 name,
1012- body : Box :: new ( Command :: Compound ( body) ) ,
1113+ body : Box :: new ( Command :: Compound ( body, Vec :: new ( ) ) ) ,
10131114 span : start_span. merge ( self . current_span ) ,
10141115 } ) )
10151116 }
@@ -1046,7 +1147,7 @@ impl<'a> Parser<'a> {
10461147
10471148 Ok ( Command :: Function ( FunctionDef {
10481149 name,
1049- body : Box :: new ( Command :: Compound ( body) ) ,
1150+ body : Box :: new ( Command :: Compound ( body, Vec :: new ( ) ) ) ,
10501151 span : start_span. merge ( self . current_span ) ,
10511152 } ) )
10521153 }
0 commit comments