mirror of
https://github.com/nushell/nushell.git
synced 2025-08-09 07:55:59 +02:00
Revert "Pipeline operators: &&
and ||
" (#7452)
Reverts nushell/nushell#7448 Some surprising behavior in how we do this. For example: ``` 〉if (true || false) { print "yes!" } else { print "no!" } no! 〉if (true or false) { print "yes!" } else { print "no!" } yes! ``` This means for folks who are using the old `||`, they possibly get the wrong answer once they upgrade. I don't think we can ship with that as it will catch too many people by surprise and just make it easier to write buggy code.
This commit is contained in:
@ -789,16 +789,14 @@ pub fn eval_element_with_input(
|
||||
redirect_stderr: bool,
|
||||
) -> Result<(PipelineData, bool), ShellError> {
|
||||
match element {
|
||||
PipelineElement::Expression(_, expr) | PipelineElement::Or(_, expr) => {
|
||||
eval_expression_with_input(
|
||||
engine_state,
|
||||
stack,
|
||||
expr,
|
||||
input,
|
||||
redirect_stdout,
|
||||
redirect_stderr,
|
||||
)
|
||||
}
|
||||
PipelineElement::Expression(_, expr) => eval_expression_with_input(
|
||||
engine_state,
|
||||
stack,
|
||||
expr,
|
||||
input,
|
||||
redirect_stdout,
|
||||
redirect_stderr,
|
||||
),
|
||||
PipelineElement::Redirection(span, redirection, expr) => match &expr.expr {
|
||||
Expr::String(_) => {
|
||||
let input = match (redirection, input) {
|
||||
@ -905,6 +903,22 @@ pub fn eval_element_with_input(
|
||||
}
|
||||
_ => Err(ShellError::CommandNotFound(*span)),
|
||||
},
|
||||
PipelineElement::And(_, expr) => eval_expression_with_input(
|
||||
engine_state,
|
||||
stack,
|
||||
expr,
|
||||
input,
|
||||
redirect_stdout,
|
||||
redirect_stderr,
|
||||
),
|
||||
PipelineElement::Or(_, expr) => eval_expression_with_input(
|
||||
engine_state,
|
||||
stack,
|
||||
expr,
|
||||
input,
|
||||
redirect_stdout,
|
||||
redirect_stderr,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
@ -938,16 +952,10 @@ pub fn eval_block(
|
||||
redirect_stderr: bool,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let num_pipelines = block.len();
|
||||
let mut pipeline_idx = 0;
|
||||
|
||||
while pipeline_idx < block.pipelines.len() {
|
||||
let pipeline = &block.pipelines[pipeline_idx];
|
||||
|
||||
for (pipeline_idx, pipeline) in block.pipelines.iter().enumerate() {
|
||||
let mut i = 0;
|
||||
|
||||
let mut last_element_is_pipeline_or = false;
|
||||
|
||||
while i < pipeline.elements.len() && !last_element_is_pipeline_or {
|
||||
while i < pipeline.elements.len() {
|
||||
let redirect_stderr = redirect_stderr
|
||||
|| ((i < pipeline.elements.len() - 1)
|
||||
&& (matches!(
|
||||
@ -956,8 +964,6 @@ pub fn eval_block(
|
||||
| PipelineElement::Redirection(_, Redirection::StdoutAndStderr, _)
|
||||
)));
|
||||
|
||||
last_element_is_pipeline_or = matches!(&pipeline.elements[i], PipelineElement::Or(..));
|
||||
|
||||
// if eval internal command failed, it can just make early return with `Err(ShellError)`.
|
||||
let eval_result = eval_element_with_input(
|
||||
engine_state,
|
||||
@ -984,16 +990,10 @@ pub fn eval_block(
|
||||
// don't return `Err(ShellError)`, so nushell wouldn't show extra error message.
|
||||
}
|
||||
(Err(error), true) => input = PipelineData::Value(Value::Error { error }, None),
|
||||
(output, false) if last_element_is_pipeline_or => match output {
|
||||
Ok(output) => {
|
||||
input = output.0;
|
||||
}
|
||||
Err(error) => input = PipelineData::Value(Value::Error { error }, None),
|
||||
},
|
||||
(output, false) => {
|
||||
let output = output?;
|
||||
input = output.0;
|
||||
// external command may have failed
|
||||
// external command may runs to failed
|
||||
// make early return so remaining commands will not be executed.
|
||||
// don't return `Err(ShellError)`, so nushell wouldn't show extra error message.
|
||||
if output.1 {
|
||||
@ -1006,109 +1006,83 @@ pub fn eval_block(
|
||||
}
|
||||
|
||||
if pipeline_idx < (num_pipelines) - 1 {
|
||||
if last_element_is_pipeline_or {
|
||||
let input_is_error = matches!(input, PipelineData::Value(Value::Error { .. }, ..));
|
||||
match input {
|
||||
PipelineData::Value(Value::Nothing { .. }, ..) => {}
|
||||
PipelineData::ExternalStream {
|
||||
ref mut exit_code, ..
|
||||
} => {
|
||||
let exit_code = exit_code.take();
|
||||
|
||||
let result =
|
||||
drain_and_print(engine_state, stack, input, redirect_stdout, redirect_stderr);
|
||||
// Drain the input to the screen via tabular output
|
||||
let config = engine_state.get_config();
|
||||
|
||||
let last_exit_code = stack.last_exit_code(engine_state).unwrap_or(0);
|
||||
match engine_state.find_decl("table".as_bytes(), &[]) {
|
||||
Some(decl_id) => {
|
||||
let table = engine_state.get_decl(decl_id).run(
|
||||
engine_state,
|
||||
stack,
|
||||
&Call::new(Span::new(0, 0)),
|
||||
input,
|
||||
)?;
|
||||
|
||||
if last_exit_code == 0 && result.is_ok() && !input_is_error {
|
||||
// Skip the next pipeline ot run because this pipeline was successful and the
|
||||
// user used the `a || b` connector
|
||||
pipeline_idx += 1;
|
||||
print_or_return(table, config)?;
|
||||
}
|
||||
None => {
|
||||
print_or_return(input, config)?;
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(exit_code) = exit_code {
|
||||
let mut v: Vec<_> = exit_code.collect();
|
||||
|
||||
if let Some(v) = v.pop() {
|
||||
stack.add_env_var("LAST_EXIT_CODE".into(), v);
|
||||
}
|
||||
}
|
||||
}
|
||||
input = PipelineData::empty()
|
||||
} else {
|
||||
drain_and_print(engine_state, stack, input, redirect_stdout, redirect_stderr)?;
|
||||
_ => {
|
||||
// Drain the input to the screen via tabular output
|
||||
let config = engine_state.get_config();
|
||||
|
||||
input = PipelineData::empty()
|
||||
match engine_state.find_decl("table".as_bytes(), &[]) {
|
||||
Some(decl_id) => {
|
||||
let table = engine_state.get_decl(decl_id);
|
||||
|
||||
if let Some(block_id) = table.get_block_id() {
|
||||
let block = engine_state.get_block(block_id);
|
||||
eval_block(
|
||||
engine_state,
|
||||
stack,
|
||||
block,
|
||||
input,
|
||||
redirect_stdout,
|
||||
redirect_stderr,
|
||||
)?;
|
||||
} else {
|
||||
let table = table.run(
|
||||
engine_state,
|
||||
stack,
|
||||
&Call::new(Span::new(0, 0)),
|
||||
input,
|
||||
)?;
|
||||
|
||||
print_or_return(table, config)?;
|
||||
}
|
||||
}
|
||||
None => {
|
||||
print_or_return(input, config)?;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pipeline_idx += 1;
|
||||
input = PipelineData::empty()
|
||||
}
|
||||
}
|
||||
|
||||
Ok(input)
|
||||
}
|
||||
|
||||
fn drain_and_print(
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
mut input: PipelineData,
|
||||
redirect_stdout: bool,
|
||||
redirect_stderr: bool,
|
||||
) -> Result<(), ShellError> {
|
||||
match input {
|
||||
PipelineData::Value(Value::Nothing { .. }, ..) => {}
|
||||
PipelineData::ExternalStream {
|
||||
ref mut exit_code, ..
|
||||
} => {
|
||||
let exit_code = exit_code.take();
|
||||
|
||||
// Drain the input to the screen via tabular output
|
||||
let config = engine_state.get_config();
|
||||
|
||||
match engine_state.find_decl("table".as_bytes(), &[]) {
|
||||
Some(decl_id) => {
|
||||
let table = engine_state.get_decl(decl_id).run(
|
||||
engine_state,
|
||||
stack,
|
||||
&Call::new(Span::new(0, 0)),
|
||||
input,
|
||||
)?;
|
||||
|
||||
print_or_return(table, config)?;
|
||||
}
|
||||
None => {
|
||||
print_or_return(input, config)?;
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(exit_code) = exit_code {
|
||||
let mut v: Vec<_> = exit_code.collect();
|
||||
|
||||
if let Some(v) = v.pop() {
|
||||
stack.add_env_var("LAST_EXIT_CODE".into(), v);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// Drain the input to the screen via tabular output
|
||||
let config = engine_state.get_config();
|
||||
|
||||
match engine_state.find_decl("table".as_bytes(), &[]) {
|
||||
Some(decl_id) => {
|
||||
let table = engine_state.get_decl(decl_id);
|
||||
|
||||
if let Some(block_id) = table.get_block_id() {
|
||||
let block = engine_state.get_block(block_id);
|
||||
eval_block(
|
||||
engine_state,
|
||||
stack,
|
||||
block,
|
||||
input,
|
||||
redirect_stdout,
|
||||
redirect_stderr,
|
||||
)?;
|
||||
} else {
|
||||
let table =
|
||||
table.run(engine_state, stack, &Call::new(Span::new(0, 0)), input)?;
|
||||
|
||||
print_or_return(table, config)?;
|
||||
}
|
||||
}
|
||||
None => {
|
||||
print_or_return(input, config)?;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn print_or_return(pipeline_data: PipelineData, config: &Config) -> Result<(), ShellError> {
|
||||
for item in pipeline_data {
|
||||
if let Value::Error { error } = item {
|
||||
|
Reference in New Issue
Block a user