mirror of
https://github.com/nushell/nushell.git
synced 2025-08-09 06:55:36 +02:00
Stdout/Stderr redirection (#7185)
This adds new pipeline connectors called out> and err> which redirect either stdout or stderr to a file. You can also use out+err> (or err+out>) to redirect both streams into a file.
This commit is contained in:
@ -32,6 +32,10 @@ pub enum FlatShape {
|
||||
GlobPattern,
|
||||
Variable,
|
||||
Flag,
|
||||
Pipe,
|
||||
And,
|
||||
Or,
|
||||
Redirection,
|
||||
Custom(DeclId),
|
||||
}
|
||||
|
||||
@ -63,6 +67,10 @@ impl Display for FlatShape {
|
||||
FlatShape::GlobPattern => write!(f, "shape_globpattern"),
|
||||
FlatShape::Variable => write!(f, "shape_variable"),
|
||||
FlatShape::Flag => write!(f, "shape_flag"),
|
||||
FlatShape::Pipe => write!(f, "shape_pipe"),
|
||||
FlatShape::And => write!(f, "shape_and"),
|
||||
FlatShape::Or => write!(f, "shape_or"),
|
||||
FlatShape::Redirection => write!(f, "shape_redirection"),
|
||||
FlatShape::Custom(_) => write!(f, "shape_custom"),
|
||||
}
|
||||
}
|
||||
@ -502,10 +510,30 @@ pub fn flatten_pipeline_element(
|
||||
pipeline_element: &PipelineElement,
|
||||
) -> Vec<(Span, FlatShape)> {
|
||||
match pipeline_element {
|
||||
PipelineElement::Expression(expr)
|
||||
| PipelineElement::Redirect(expr)
|
||||
| PipelineElement::And(expr)
|
||||
| PipelineElement::Or(expr) => flatten_expression(working_set, expr),
|
||||
PipelineElement::Expression(span, expr) => {
|
||||
if let Some(span) = span {
|
||||
let mut output = vec![(*span, FlatShape::Pipe)];
|
||||
output.append(&mut flatten_expression(working_set, expr));
|
||||
output
|
||||
} else {
|
||||
flatten_expression(working_set, expr)
|
||||
}
|
||||
}
|
||||
PipelineElement::Redirection(span, _, expr) => {
|
||||
let mut output = vec![(*span, FlatShape::Redirection)];
|
||||
output.append(&mut flatten_expression(working_set, expr));
|
||||
output
|
||||
}
|
||||
PipelineElement::And(span, expr) => {
|
||||
let mut output = vec![(*span, FlatShape::And)];
|
||||
output.append(&mut flatten_expression(working_set, expr));
|
||||
output
|
||||
}
|
||||
PipelineElement::Or(span, expr) => {
|
||||
let mut output = vec![(*span, FlatShape::Or)];
|
||||
output.append(&mut flatten_expression(working_set, expr));
|
||||
output
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,12 +1,15 @@
|
||||
use crate::ParseError;
|
||||
use nu_protocol::Span;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum TokenContents {
|
||||
Item,
|
||||
Comment,
|
||||
Pipe,
|
||||
Semicolon,
|
||||
OutGreaterThan,
|
||||
ErrGreaterThan,
|
||||
OutErrGreaterThan,
|
||||
Eol,
|
||||
}
|
||||
|
||||
@ -74,7 +77,7 @@ pub fn lex_item(
|
||||
span_offset: usize,
|
||||
additional_whitespace: &[u8],
|
||||
special_tokens: &[u8],
|
||||
) -> (Span, Option<ParseError>) {
|
||||
) -> (Token, Option<ParseError>) {
|
||||
// This variable tracks the starting character of a string literal, so that
|
||||
// we remain inside the string literal lexer mode until we encounter the
|
||||
// closing quote.
|
||||
@ -113,7 +116,10 @@ pub fn lex_item(
|
||||
let span = Span::new(span_offset + token_start, span_offset + *curr_offset);
|
||||
|
||||
return (
|
||||
span,
|
||||
Token {
|
||||
contents: TokenContents::Item,
|
||||
span,
|
||||
},
|
||||
Some(ParseError::UnexpectedEof(
|
||||
(start as char).to_string(),
|
||||
Span {
|
||||
@ -195,7 +201,13 @@ pub fn lex_item(
|
||||
},
|
||||
);
|
||||
|
||||
return (span, Some(cause));
|
||||
return (
|
||||
Token {
|
||||
contents: TokenContents::Item,
|
||||
span,
|
||||
},
|
||||
Some(cause),
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(delim) = quote_start {
|
||||
@ -203,7 +215,10 @@ pub fn lex_item(
|
||||
// anyone wanting to consume this partial parse (e.g., completions) will be able to get
|
||||
// correct information from the non-lite parse.
|
||||
return (
|
||||
span,
|
||||
Token {
|
||||
contents: TokenContents::Item,
|
||||
span,
|
||||
},
|
||||
Some(ParseError::UnexpectedEof(
|
||||
(delim as char).to_string(),
|
||||
Span {
|
||||
@ -217,12 +232,44 @@ pub fn lex_item(
|
||||
// If we didn't accumulate any characters, it's an unexpected error.
|
||||
if *curr_offset - token_start == 0 {
|
||||
return (
|
||||
span,
|
||||
Token {
|
||||
contents: TokenContents::Item,
|
||||
span,
|
||||
},
|
||||
Some(ParseError::UnexpectedEof("command".to_string(), span)),
|
||||
);
|
||||
}
|
||||
|
||||
(span, None)
|
||||
match &input[(span.start - span_offset)..(span.end - span_offset)] {
|
||||
b"out>" => (
|
||||
Token {
|
||||
contents: TokenContents::OutGreaterThan,
|
||||
span,
|
||||
},
|
||||
None,
|
||||
),
|
||||
b"err>" => (
|
||||
Token {
|
||||
contents: TokenContents::ErrGreaterThan,
|
||||
span,
|
||||
},
|
||||
None,
|
||||
),
|
||||
b"out+err>" | b"err+out>" => (
|
||||
Token {
|
||||
contents: TokenContents::OutErrGreaterThan,
|
||||
span,
|
||||
},
|
||||
None,
|
||||
),
|
||||
_ => (
|
||||
Token {
|
||||
contents: TokenContents::Item,
|
||||
span,
|
||||
},
|
||||
None,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lex(
|
||||
@ -347,7 +394,7 @@ pub fn lex(
|
||||
} else {
|
||||
// Otherwise, try to consume an unclassified token.
|
||||
|
||||
let (span, err) = lex_item(
|
||||
let (token, err) = lex_item(
|
||||
input,
|
||||
&mut curr_offset,
|
||||
span_offset,
|
||||
@ -358,7 +405,7 @@ pub fn lex(
|
||||
error = err;
|
||||
}
|
||||
is_complete = true;
|
||||
output.push(Token::new(TokenContents::Item, span));
|
||||
output.push(token);
|
||||
}
|
||||
}
|
||||
(output, error)
|
||||
|
@ -865,10 +865,13 @@ pub fn parse_export_in_module(
|
||||
};
|
||||
|
||||
// Trying to warp the 'def' call into the 'export def' in a very clumsy way
|
||||
if let Some(PipelineElement::Expression(Expression {
|
||||
expr: Expr::Call(ref def_call),
|
||||
..
|
||||
})) = pipeline.elements.get(0)
|
||||
if let Some(PipelineElement::Expression(
|
||||
_,
|
||||
Expression {
|
||||
expr: Expr::Call(ref def_call),
|
||||
..
|
||||
},
|
||||
)) = pipeline.elements.get(0)
|
||||
{
|
||||
call = def_call.clone();
|
||||
|
||||
@ -930,10 +933,13 @@ pub fn parse_export_in_module(
|
||||
};
|
||||
|
||||
// Trying to warp the 'def' call into the 'export def' in a very clumsy way
|
||||
if let Some(PipelineElement::Expression(Expression {
|
||||
expr: Expr::Call(ref def_call),
|
||||
..
|
||||
})) = pipeline.elements.get(0)
|
||||
if let Some(PipelineElement::Expression(
|
||||
_,
|
||||
Expression {
|
||||
expr: Expr::Call(ref def_call),
|
||||
..
|
||||
},
|
||||
)) = pipeline.elements.get(0)
|
||||
{
|
||||
call = def_call.clone();
|
||||
|
||||
@ -996,10 +1002,13 @@ pub fn parse_export_in_module(
|
||||
};
|
||||
|
||||
// Trying to warp the 'def' call into the 'export def' in a very clumsy way
|
||||
if let Some(PipelineElement::Expression(Expression {
|
||||
expr: Expr::Call(ref def_call),
|
||||
..
|
||||
})) = pipeline.elements.get(0)
|
||||
if let Some(PipelineElement::Expression(
|
||||
_,
|
||||
Expression {
|
||||
expr: Expr::Call(ref def_call),
|
||||
..
|
||||
},
|
||||
)) = pipeline.elements.get(0)
|
||||
{
|
||||
call = def_call.clone();
|
||||
|
||||
@ -1062,10 +1071,13 @@ pub fn parse_export_in_module(
|
||||
};
|
||||
|
||||
// Trying to warp the 'alias' call into the 'export alias' in a very clumsy way
|
||||
if let Some(PipelineElement::Expression(Expression {
|
||||
expr: Expr::Call(ref alias_call),
|
||||
..
|
||||
})) = pipeline.elements.get(0)
|
||||
if let Some(PipelineElement::Expression(
|
||||
_,
|
||||
Expression {
|
||||
expr: Expr::Call(ref alias_call),
|
||||
..
|
||||
},
|
||||
)) = pipeline.elements.get(0)
|
||||
{
|
||||
call = alias_call.clone();
|
||||
|
||||
@ -1128,10 +1140,13 @@ pub fn parse_export_in_module(
|
||||
};
|
||||
|
||||
// Trying to warp the 'use' call into the 'export use' in a very clumsy way
|
||||
if let Some(PipelineElement::Expression(Expression {
|
||||
expr: Expr::Call(ref use_call),
|
||||
..
|
||||
})) = pipeline.elements.get(0)
|
||||
if let Some(PipelineElement::Expression(
|
||||
_,
|
||||
Expression {
|
||||
expr: Expr::Call(ref use_call),
|
||||
..
|
||||
},
|
||||
)) = pipeline.elements.get(0)
|
||||
{
|
||||
call = use_call.clone();
|
||||
|
||||
@ -1313,7 +1328,7 @@ pub fn parse_module_block(
|
||||
|
||||
for pipeline in &output.block {
|
||||
if pipeline.commands.len() == 1 {
|
||||
if let LiteElement::Command(command) = &pipeline.commands[0] {
|
||||
if let LiteElement::Command(_, command) = &pipeline.commands[0] {
|
||||
parse_def_predecl(working_set, &command.parts, expand_aliases_denylist);
|
||||
}
|
||||
}
|
||||
@ -1327,7 +1342,7 @@ pub fn parse_module_block(
|
||||
.map(|pipeline| {
|
||||
if pipeline.commands.len() == 1 {
|
||||
match &pipeline.commands[0] {
|
||||
LiteElement::Command(command) => {
|
||||
LiteElement::Command(_, command) => {
|
||||
let name = working_set.get_span_contents(command.parts[0]);
|
||||
|
||||
let (pipeline, err) = match name {
|
||||
@ -1407,9 +1422,9 @@ pub fn parse_module_block(
|
||||
|
||||
pipeline
|
||||
}
|
||||
LiteElement::Or(command)
|
||||
| LiteElement::And(command)
|
||||
| LiteElement::Redirection(command) => (garbage_pipeline(&command.parts)),
|
||||
LiteElement::Or(_, command)
|
||||
| LiteElement::And(_, command)
|
||||
| LiteElement::Redirection(_, _, command) => (garbage_pipeline(&command.parts)),
|
||||
}
|
||||
} else {
|
||||
error = Some(ParseError::Expected("not a pipeline".into(), span));
|
||||
|
@ -8,7 +8,7 @@ use nu_protocol::{
|
||||
ast::{
|
||||
Argument, Assignment, Bits, Block, Boolean, Call, CellPath, Comparison, Expr, Expression,
|
||||
FullCellPath, ImportPattern, ImportPatternHead, ImportPatternMember, Math, Operator,
|
||||
PathMember, Pipeline, PipelineElement, RangeInclusion, RangeOperator,
|
||||
PathMember, Pipeline, PipelineElement, RangeInclusion, RangeOperator, Redirection,
|
||||
},
|
||||
engine::StateWorkingSet,
|
||||
span, BlockId, Flag, PositionalArg, Signature, Span, Spanned, SyntaxShape, Type, Unit, VarId,
|
||||
@ -1056,7 +1056,7 @@ pub fn parse_call(
|
||||
let result = result.elements.remove(0);
|
||||
|
||||
// If this is the first element in a pipeline, we know it has to be an expression
|
||||
if let PipelineElement::Expression(mut result) = result {
|
||||
if let PipelineElement::Expression(_, mut result) = result {
|
||||
result.replace_span(working_set, expansion_span, orig_span);
|
||||
|
||||
return (result, err);
|
||||
@ -1203,7 +1203,12 @@ fn parse_binary_with_base(
|
||||
Some(ParseError::Expected("binary".into(), span)),
|
||||
);
|
||||
}
|
||||
TokenContents::Comment | TokenContents::Semicolon | TokenContents::Eol => {}
|
||||
TokenContents::Comment
|
||||
| TokenContents::Semicolon
|
||||
| TokenContents::Eol
|
||||
| TokenContents::OutGreaterThan
|
||||
| TokenContents::ErrGreaterThan
|
||||
| TokenContents::OutErrGreaterThan => {}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1924,7 +1929,7 @@ pub fn parse_full_cell_path(
|
||||
.last()
|
||||
.and_then(|Pipeline { elements, .. }| elements.last())
|
||||
.map(|element| match element {
|
||||
PipelineElement::Expression(expr)
|
||||
PipelineElement::Expression(_, expr)
|
||||
if matches!(
|
||||
expr,
|
||||
Expression {
|
||||
@ -3071,7 +3076,7 @@ pub fn parse_row_condition(
|
||||
let mut pipeline = Pipeline::new();
|
||||
pipeline
|
||||
.elements
|
||||
.push(PipelineElement::Expression(expression));
|
||||
.push(PipelineElement::Expression(None, expression));
|
||||
|
||||
block.pipelines.push(pipeline);
|
||||
|
||||
@ -3721,7 +3726,7 @@ pub fn parse_list_expression(
|
||||
for arg in &output.block[0].commands {
|
||||
let mut spans_idx = 0;
|
||||
|
||||
if let LiteElement::Command(command) = arg {
|
||||
if let LiteElement::Command(_, command) = arg {
|
||||
while spans_idx < command.parts.len() {
|
||||
let (arg, err) = parse_multispan_value(
|
||||
working_set,
|
||||
@ -3814,10 +3819,10 @@ pub fn parse_table_expression(
|
||||
}
|
||||
_ => {
|
||||
match &output.block[0].commands[0] {
|
||||
LiteElement::Command(command)
|
||||
| LiteElement::Redirection(command)
|
||||
| LiteElement::And(command)
|
||||
| LiteElement::Or(command) => {
|
||||
LiteElement::Command(_, command)
|
||||
| LiteElement::Redirection(_, _, command)
|
||||
| LiteElement::And(_, command)
|
||||
| LiteElement::Or(_, command) => {
|
||||
let mut table_headers = vec![];
|
||||
|
||||
let (headers, err) = parse_value(
|
||||
@ -3837,10 +3842,10 @@ pub fn parse_table_expression(
|
||||
}
|
||||
|
||||
match &output.block[1].commands[0] {
|
||||
LiteElement::Command(command)
|
||||
| LiteElement::Redirection(command)
|
||||
| LiteElement::And(command)
|
||||
| LiteElement::Or(command) => {
|
||||
LiteElement::Command(_, command)
|
||||
| LiteElement::Redirection(_, _, command)
|
||||
| LiteElement::And(_, command)
|
||||
| LiteElement::Or(_, command) => {
|
||||
let mut rows = vec![];
|
||||
for part in &command.parts {
|
||||
let (values, err) = parse_value(
|
||||
@ -5184,10 +5189,10 @@ pub fn parse_block(
|
||||
for pipeline in &lite_block.block {
|
||||
if pipeline.commands.len() == 1 {
|
||||
match &pipeline.commands[0] {
|
||||
LiteElement::Command(command)
|
||||
| LiteElement::Redirection(command)
|
||||
| LiteElement::And(command)
|
||||
| LiteElement::Or(command) => {
|
||||
LiteElement::Command(_, command)
|
||||
| LiteElement::Redirection(_, _, command)
|
||||
| LiteElement::And(_, command)
|
||||
| LiteElement::Or(_, command) => {
|
||||
if let Some(err) =
|
||||
parse_def_predecl(working_set, &command.parts, expand_aliases_denylist)
|
||||
{
|
||||
@ -5208,7 +5213,7 @@ pub fn parse_block(
|
||||
.commands
|
||||
.iter()
|
||||
.map(|command| match command {
|
||||
LiteElement::Command(command) => {
|
||||
LiteElement::Command(span, command) => {
|
||||
let (expr, err) = parse_expression(
|
||||
working_set,
|
||||
&command.parts,
|
||||
@ -5221,12 +5226,12 @@ pub fn parse_block(
|
||||
error = err;
|
||||
}
|
||||
|
||||
PipelineElement::Expression(expr)
|
||||
PipelineElement::Expression(*span, expr)
|
||||
}
|
||||
LiteElement::Redirection(command) => {
|
||||
let (expr, err) = parse_expression(
|
||||
LiteElement::Redirection(span, redirection, command) => {
|
||||
let (expr, err) = parse_string(
|
||||
working_set,
|
||||
&command.parts,
|
||||
command.parts[0],
|
||||
expand_aliases_denylist,
|
||||
);
|
||||
|
||||
@ -5236,9 +5241,9 @@ pub fn parse_block(
|
||||
error = err;
|
||||
}
|
||||
|
||||
PipelineElement::Redirect(expr)
|
||||
PipelineElement::Redirection(*span, redirection.clone(), expr)
|
||||
}
|
||||
LiteElement::And(command) => {
|
||||
LiteElement::And(span, command) => {
|
||||
let (expr, err) = parse_expression(
|
||||
working_set,
|
||||
&command.parts,
|
||||
@ -5251,9 +5256,9 @@ pub fn parse_block(
|
||||
error = err;
|
||||
}
|
||||
|
||||
PipelineElement::And(expr)
|
||||
PipelineElement::And(*span, expr)
|
||||
}
|
||||
LiteElement::Or(command) => {
|
||||
LiteElement::Or(span, command) => {
|
||||
let (expr, err) = parse_expression(
|
||||
working_set,
|
||||
&command.parts,
|
||||
@ -5266,7 +5271,7 @@ pub fn parse_block(
|
||||
error = err;
|
||||
}
|
||||
|
||||
PipelineElement::Or(expr)
|
||||
PipelineElement::Or(*span, expr)
|
||||
}
|
||||
})
|
||||
.collect::<Vec<PipelineElement>>();
|
||||
@ -5288,10 +5293,10 @@ pub fn parse_block(
|
||||
Pipeline { elements: output }
|
||||
} else {
|
||||
match &pipeline.commands[0] {
|
||||
LiteElement::Command(command)
|
||||
| LiteElement::Redirection(command)
|
||||
| LiteElement::And(command)
|
||||
| LiteElement::Or(command) => {
|
||||
LiteElement::Command(_, command)
|
||||
| LiteElement::Redirection(_, _, command)
|
||||
| LiteElement::And(_, command)
|
||||
| LiteElement::Or(_, command) => {
|
||||
let (mut pipeline, err) =
|
||||
parse_builtin_commands(working_set, command, expand_aliases_denylist);
|
||||
|
||||
@ -5301,10 +5306,13 @@ pub fn parse_block(
|
||||
working_set.find_decl(b"let-env", &Type::Any)
|
||||
{
|
||||
for element in pipeline.elements.iter_mut() {
|
||||
if let PipelineElement::Expression(Expression {
|
||||
expr: Expr::Call(call),
|
||||
..
|
||||
}) = element
|
||||
if let PipelineElement::Expression(
|
||||
_,
|
||||
Expression {
|
||||
expr: Expr::Call(call),
|
||||
..
|
||||
},
|
||||
) = element
|
||||
{
|
||||
if call.decl_id == let_decl_id
|
||||
|| call.decl_id == let_env_decl_id
|
||||
@ -5421,10 +5429,10 @@ pub fn discover_captures_in_pipeline_element(
|
||||
seen_blocks: &mut HashMap<BlockId, Vec<(VarId, Span)>>,
|
||||
) -> Result<Vec<(VarId, Span)>, ParseError> {
|
||||
match element {
|
||||
PipelineElement::Expression(expression)
|
||||
| PipelineElement::Redirect(expression)
|
||||
| PipelineElement::And(expression)
|
||||
| PipelineElement::Or(expression) => {
|
||||
PipelineElement::Expression(_, expression)
|
||||
| PipelineElement::Redirection(_, _, expression)
|
||||
| PipelineElement::And(_, expression)
|
||||
| PipelineElement::Or(_, expression) => {
|
||||
discover_captures_in_expr(working_set, expression, seen, seen_blocks)
|
||||
}
|
||||
}
|
||||
@ -5674,17 +5682,21 @@ fn wrap_element_with_collect(
|
||||
element: &PipelineElement,
|
||||
) -> PipelineElement {
|
||||
match element {
|
||||
PipelineElement::Expression(expression) => {
|
||||
PipelineElement::Expression(wrap_expr_with_collect(working_set, expression))
|
||||
PipelineElement::Expression(span, expression) => {
|
||||
PipelineElement::Expression(*span, wrap_expr_with_collect(working_set, expression))
|
||||
}
|
||||
PipelineElement::Redirect(expression) => {
|
||||
PipelineElement::Redirect(wrap_expr_with_collect(working_set, expression))
|
||||
PipelineElement::Redirection(span, redirection, expression) => {
|
||||
PipelineElement::Redirection(
|
||||
*span,
|
||||
redirection.clone(),
|
||||
wrap_expr_with_collect(working_set, expression),
|
||||
)
|
||||
}
|
||||
PipelineElement::And(expression) => {
|
||||
PipelineElement::And(wrap_expr_with_collect(working_set, expression))
|
||||
PipelineElement::And(span, expression) => {
|
||||
PipelineElement::And(*span, wrap_expr_with_collect(working_set, expression))
|
||||
}
|
||||
PipelineElement::Or(expression) => {
|
||||
PipelineElement::Or(wrap_expr_with_collect(working_set, expression))
|
||||
PipelineElement::Or(span, expression) => {
|
||||
PipelineElement::Or(*span, wrap_expr_with_collect(working_set, expression))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -5781,12 +5793,13 @@ impl LiteCommand {
|
||||
}
|
||||
}
|
||||
|
||||
// Note: the Span is the span of the connector not the whole element
|
||||
#[derive(Debug)]
|
||||
pub enum LiteElement {
|
||||
Command(LiteCommand),
|
||||
Redirection(LiteCommand),
|
||||
And(LiteCommand),
|
||||
Or(LiteCommand),
|
||||
Command(Option<Span>, LiteCommand),
|
||||
Redirection(Span, Redirection, LiteCommand),
|
||||
And(Span, LiteCommand),
|
||||
Or(Span, LiteCommand),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -5846,6 +5859,13 @@ pub fn lite_parse(tokens: &[Token]) -> (LiteBlock, Option<ParseError>) {
|
||||
|
||||
let mut last_token = TokenContents::Eol;
|
||||
|
||||
let mut last_connector = TokenContents::Pipe;
|
||||
let mut last_connector_span: Option<Span> = None;
|
||||
|
||||
if tokens.is_empty() {
|
||||
return (LiteBlock::new(), None);
|
||||
}
|
||||
|
||||
let mut curr_comment: Option<Vec<Span>> = None;
|
||||
|
||||
for token in tokens.iter() {
|
||||
@ -5858,17 +5878,96 @@ pub fn lite_parse(tokens: &[Token]) -> (LiteBlock, Option<ParseError>) {
|
||||
curr_command.push(token.span);
|
||||
last_token = TokenContents::Item;
|
||||
}
|
||||
TokenContents::OutGreaterThan
|
||||
| TokenContents::ErrGreaterThan
|
||||
| TokenContents::OutErrGreaterThan => {
|
||||
if !curr_command.is_empty() {
|
||||
match last_connector {
|
||||
TokenContents::OutGreaterThan => {
|
||||
curr_pipeline.push(LiteElement::Redirection(
|
||||
token.span,
|
||||
Redirection::Stdout,
|
||||
curr_command,
|
||||
));
|
||||
}
|
||||
TokenContents::ErrGreaterThan => {
|
||||
curr_pipeline.push(LiteElement::Redirection(
|
||||
token.span,
|
||||
Redirection::Stderr,
|
||||
curr_command,
|
||||
));
|
||||
}
|
||||
TokenContents::OutErrGreaterThan => {
|
||||
curr_pipeline.push(LiteElement::Redirection(
|
||||
token.span,
|
||||
Redirection::StdoutAndStderr,
|
||||
curr_command,
|
||||
));
|
||||
}
|
||||
_ => {
|
||||
curr_pipeline
|
||||
.push(LiteElement::Command(last_connector_span, curr_command));
|
||||
}
|
||||
}
|
||||
curr_command = LiteCommand::new();
|
||||
}
|
||||
last_token = token.contents;
|
||||
last_connector = token.contents;
|
||||
last_connector_span = Some(token.span);
|
||||
}
|
||||
TokenContents::Pipe => {
|
||||
if !curr_command.is_empty() {
|
||||
curr_pipeline.push(LiteElement::Command(curr_command));
|
||||
match last_connector {
|
||||
TokenContents::OutGreaterThan => {
|
||||
curr_pipeline.push(LiteElement::Redirection(
|
||||
token.span,
|
||||
Redirection::Stdout,
|
||||
curr_command,
|
||||
));
|
||||
}
|
||||
TokenContents::ErrGreaterThan => {
|
||||
curr_pipeline.push(LiteElement::Redirection(
|
||||
token.span,
|
||||
Redirection::Stderr,
|
||||
curr_command,
|
||||
));
|
||||
}
|
||||
TokenContents::OutErrGreaterThan => {
|
||||
curr_pipeline.push(LiteElement::Redirection(
|
||||
token.span,
|
||||
Redirection::StdoutAndStderr,
|
||||
curr_command,
|
||||
));
|
||||
}
|
||||
_ => {
|
||||
curr_pipeline
|
||||
.push(LiteElement::Command(last_connector_span, curr_command));
|
||||
}
|
||||
}
|
||||
curr_command = LiteCommand::new();
|
||||
}
|
||||
last_token = TokenContents::Pipe;
|
||||
last_connector = TokenContents::Pipe;
|
||||
last_connector_span = Some(token.span);
|
||||
}
|
||||
TokenContents::Eol => {
|
||||
if last_token != TokenContents::Pipe {
|
||||
if last_token != TokenContents::Pipe && last_token != TokenContents::OutGreaterThan
|
||||
{
|
||||
if !curr_command.is_empty() {
|
||||
curr_pipeline.push(LiteElement::Command(curr_command));
|
||||
if last_connector == TokenContents::OutGreaterThan
|
||||
|| last_connector == TokenContents::ErrGreaterThan
|
||||
|| last_connector == TokenContents::OutErrGreaterThan
|
||||
{
|
||||
curr_pipeline.push(LiteElement::Redirection(
|
||||
last_connector_span
|
||||
.expect("internal error: redirection missing span information"),
|
||||
Redirection::Stdout,
|
||||
curr_command,
|
||||
));
|
||||
} else {
|
||||
curr_pipeline
|
||||
.push(LiteElement::Command(last_connector_span, curr_command));
|
||||
}
|
||||
|
||||
curr_command = LiteCommand::new();
|
||||
}
|
||||
@ -5889,7 +5988,19 @@ pub fn lite_parse(tokens: &[Token]) -> (LiteBlock, Option<ParseError>) {
|
||||
}
|
||||
TokenContents::Semicolon => {
|
||||
if !curr_command.is_empty() {
|
||||
curr_pipeline.push(LiteElement::Command(curr_command));
|
||||
if last_connector == TokenContents::OutGreaterThan
|
||||
|| last_connector == TokenContents::ErrGreaterThan
|
||||
|| last_connector == TokenContents::OutErrGreaterThan
|
||||
{
|
||||
curr_pipeline.push(LiteElement::Redirection(
|
||||
last_connector_span
|
||||
.expect("internal error: redirection missing span information"),
|
||||
Redirection::Stdout,
|
||||
curr_command,
|
||||
));
|
||||
} else {
|
||||
curr_pipeline.push(LiteElement::Command(last_connector_span, curr_command));
|
||||
}
|
||||
|
||||
curr_command = LiteCommand::new();
|
||||
}
|
||||
@ -5922,7 +6033,35 @@ pub fn lite_parse(tokens: &[Token]) -> (LiteBlock, Option<ParseError>) {
|
||||
}
|
||||
|
||||
if !curr_command.is_empty() {
|
||||
curr_pipeline.push(LiteElement::Command(curr_command));
|
||||
match last_connector {
|
||||
TokenContents::OutGreaterThan => {
|
||||
curr_pipeline.push(LiteElement::Redirection(
|
||||
last_connector_span
|
||||
.expect("internal error: redirection missing span information"),
|
||||
Redirection::Stdout,
|
||||
curr_command,
|
||||
));
|
||||
}
|
||||
TokenContents::ErrGreaterThan => {
|
||||
curr_pipeline.push(LiteElement::Redirection(
|
||||
last_connector_span
|
||||
.expect("internal error: redirection missing span information"),
|
||||
Redirection::Stderr,
|
||||
curr_command,
|
||||
));
|
||||
}
|
||||
TokenContents::OutErrGreaterThan => {
|
||||
curr_pipeline.push(LiteElement::Redirection(
|
||||
last_connector_span
|
||||
.expect("internal error: redirection missing span information"),
|
||||
Redirection::StdoutAndStderr,
|
||||
curr_command,
|
||||
));
|
||||
}
|
||||
_ => {
|
||||
curr_pipeline.push(LiteElement::Command(last_connector_span, curr_command));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !curr_pipeline.is_empty() {
|
||||
|
Reference in New Issue
Block a user