Support o>>, e>>, o+e>> to append output to an external file (#10764)

# Description
Close: #10278

This pr introduces `o>>`, `e>>`, `o+e>>` to allow redirection to append
to a file.
Examples:
```nushell
echo abc o>> a.txt
echo abc o>> a.txt
cat asdf e>> a.txt
cat asdf e>> a.txt
cat asdf o+e>> a.txt
```

~~TODO:~~
~~1. currently internal commands with `o+e>` redirect to a variable is
broken: `let x = "a.txt"; echo abc o+e> $x`, not sure when it was
introduced...~~
~~2. redirect stdout and stderr with append mode doesn't supported yet:
`cat asdf o>>a.txt e>>b.ext`~~

~~For these 2 items, I'd like to fix them in different prs.~~
Already done in this pr
This commit is contained in:
WindSoilder
2023-11-27 21:52:39 +08:00
committed by GitHub
parent 1ff8c2d81d
commit 077d1c8125
13 changed files with 364 additions and 147 deletions

View File

@ -71,7 +71,7 @@ impl Block {
if let Some(last) = last.elements.last() {
match last {
PipelineElement::Expression(_, expr) => expr.ty.clone(),
PipelineElement::Redirection(_, _, _) => Type::Any,
PipelineElement::Redirection(_, _, _, _) => Type::Any,
PipelineElement::SeparateRedirection { .. } => Type::Any,
PipelineElement::SameTargetRedirection { .. } => Type::Any,
PipelineElement::And(_, expr) => expr.ty.clone(),

View File

@ -13,14 +13,17 @@ pub enum Redirection {
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum PipelineElement {
Expression(Option<Span>, Expression),
Redirection(Span, Redirection, Expression),
// final field indicates if it's in append mode
Redirection(Span, Redirection, Expression, bool),
// final bool field indicates if it's in append mode
SeparateRedirection {
out: (Span, Expression),
err: (Span, Expression),
out: (Span, Expression, bool),
err: (Span, Expression, bool),
},
// redirection's final bool field indicates if it's in append mode
SameTargetRedirection {
cmd: (Option<Span>, Expression),
redirection: (Span, Expression),
redirection: (Span, Expression, bool),
},
And(Span, Expression),
Or(Span, Expression),
@ -30,9 +33,9 @@ impl PipelineElement {
pub fn expression(&self) -> &Expression {
match self {
PipelineElement::Expression(_, expression) => expression,
PipelineElement::Redirection(_, _, expression) => expression,
PipelineElement::Redirection(_, _, expression, _) => expression,
PipelineElement::SeparateRedirection {
out: (_, expression),
out: (_, expression, _),
..
} => expression,
PipelineElement::SameTargetRedirection {
@ -52,9 +55,9 @@ impl PipelineElement {
..
} => expression.span,
PipelineElement::Expression(Some(span), expression)
| PipelineElement::Redirection(span, _, expression)
| PipelineElement::Redirection(span, _, expression, _)
| PipelineElement::SeparateRedirection {
out: (span, expression),
out: (span, expression, _),
..
}
| PipelineElement::And(span, expression)
@ -71,7 +74,7 @@ impl PipelineElement {
pub fn has_in_variable(&self, working_set: &StateWorkingSet) -> bool {
match self {
PipelineElement::Expression(_, expression)
| PipelineElement::Redirection(_, _, expression)
| PipelineElement::Redirection(_, _, expression, _)
| PipelineElement::And(_, expression)
| PipelineElement::Or(_, expression)
| PipelineElement::SameTargetRedirection {
@ -79,8 +82,8 @@ impl PipelineElement {
..
} => expression.has_in_variable(working_set),
PipelineElement::SeparateRedirection {
out: (_, out_expr),
err: (_, err_expr),
out: (_, out_expr, _),
err: (_, err_expr, _),
} => out_expr.has_in_variable(working_set) || err_expr.has_in_variable(working_set),
}
}
@ -88,7 +91,7 @@ impl PipelineElement {
pub fn replace_in_variable(&mut self, working_set: &mut StateWorkingSet, new_var_id: VarId) {
match self {
PipelineElement::Expression(_, expression)
| PipelineElement::Redirection(_, _, expression)
| PipelineElement::Redirection(_, _, expression, _)
| PipelineElement::And(_, expression)
| PipelineElement::Or(_, expression)
| PipelineElement::SameTargetRedirection {
@ -96,8 +99,8 @@ impl PipelineElement {
..
} => expression.replace_in_variable(working_set, new_var_id),
PipelineElement::SeparateRedirection {
out: (_, out_expr),
err: (_, err_expr),
out: (_, out_expr, _),
err: (_, err_expr, _),
} => {
if out_expr.has_in_variable(working_set) {
out_expr.replace_in_variable(working_set, new_var_id)
@ -117,7 +120,7 @@ impl PipelineElement {
) {
match self {
PipelineElement::Expression(_, expression)
| PipelineElement::Redirection(_, _, expression)
| PipelineElement::Redirection(_, _, expression, _)
| PipelineElement::And(_, expression)
| PipelineElement::Or(_, expression)
| PipelineElement::SameTargetRedirection {
@ -125,7 +128,7 @@ impl PipelineElement {
..
}
| PipelineElement::SeparateRedirection {
out: (_, expression),
out: (_, expression, _),
..
} => expression.replace_span(working_set, replaced, new_span),
}