add -e flag to print, to print the value to stderr (#5935)

* Refactor: make stdout write all and flush as generic function

* support print to stderr
This commit is contained in:
WindSoilder 2022-07-02 22:54:49 +08:00 committed by GitHub
parent be7f35246e
commit 84caf8859f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 64 additions and 16 deletions

View file

@ -21,6 +21,7 @@ impl Command for Print {
"print without inserting a newline for the line ending", "print without inserting a newline for the line ending",
Some('n'), Some('n'),
) )
.switch("stderr", "print to stderr instead of stdout", Some('e'))
.category(Category::Strings) .category(Category::Strings)
} }
@ -48,11 +49,12 @@ Since this command has no output, there is no point in piping it with other comm
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let args: Vec<Value> = call.rest(engine_state, stack, 0)?; let args: Vec<Value> = call.rest(engine_state, stack, 0)?;
let no_newline = call.has_flag("no-newline"); let no_newline = call.has_flag("no-newline");
let to_stderr = call.has_flag("stderr");
let head = call.head; let head = call.head;
for arg in args { for arg in args {
arg.into_pipeline_data() arg.into_pipeline_data()
.print(engine_state, stack, no_newline)?; .print(engine_state, stack, no_newline, to_stderr)?;
} }
Ok(PipelineData::new(head)) Ok(PipelineData::new(head))

View file

@ -247,7 +247,7 @@ pub fn eval_source(
set_last_exit_code(stack, 0); set_last_exit_code(stack, 0);
} }
if let Err(err) = pipeline_data.print(engine_state, stack, false) { if let Err(err) = pipeline_data.print(engine_state, stack, false, false) {
let working_set = StateWorkingSet::new(engine_state); let working_set = StateWorkingSet::new(engine_state);
report_error(&working_set, &err); report_error(&working_set, &err);

View file

@ -216,7 +216,7 @@ impl Command for Watch {
match eval_result { match eval_result {
Ok(val) => { Ok(val) => {
val.print(engine_state, stack, false)?; val.print(engine_state, stack, false, false)?;
} }
Err(err) => { Err(err) => {
let working_set = StateWorkingSet::new(engine_state); let working_set = StateWorkingSet::new(engine_state);

View file

@ -44,6 +44,7 @@ mod open;
mod parse; mod parse;
mod path; mod path;
mod prepend; mod prepend;
mod print;
#[cfg(feature = "database")] #[cfg(feature = "database")]
mod query; mod query;
mod random; mod random;

View file

@ -0,0 +1,23 @@
use nu_test_support::{nu, pipeline};
#[test]
fn print_to_stdout() {
let actual = nu!(
cwd: ".", pipeline(
"print 'hello world'"
)
);
assert!(actual.out.contains("hello world"));
assert!(actual.err.is_empty());
}
#[test]
fn print_to_stderr() {
let actual = nu!(
cwd: ".", pipeline(
"print -e 'hello world'"
)
);
assert!(actual.out.is_empty());
assert!(actual.err.contains("hello world"));
}

View file

@ -3,7 +3,7 @@ use crate::{
engine::{EngineState, Stack, StateWorkingSet}, engine::{EngineState, Stack, StateWorkingSet},
format_error, Config, ListStream, RawStream, ShellError, Span, Value, format_error, Config, ListStream, RawStream, ShellError, Span, Value,
}; };
use nu_utils::{stdout_write_all_and_flush, stdout_write_all_as_binary_and_flush}; use nu_utils::{stderr_write_all_and_flush, stdout_write_all_and_flush};
use std::sync::{atomic::AtomicBool, Arc}; use std::sync::{atomic::AtomicBool, Arc};
/// The foundational abstraction for input and output to commands /// The foundational abstraction for input and output to commands
@ -414,11 +414,16 @@ impl PipelineData {
} }
} }
/// Consume and print self data immediately.
///
/// `no_newline` controls if we need to attach newline character to output.
/// `to_stderr` controls if data is output to stderr, when the value is false, the data is ouput to stdout.
pub fn print( pub fn print(
self, self,
engine_state: &EngineState, engine_state: &EngineState,
stack: &mut Stack, stack: &mut Stack,
no_newline: bool, no_newline: bool,
to_stderr: bool,
) -> Result<(), ShellError> { ) -> Result<(), ShellError> {
// If the table function is in the declarations, then we can use it // If the table function is in the declarations, then we can use it
// to create the table value that will be printed in the terminal // to create the table value that will be printed in the terminal
@ -436,7 +441,12 @@ impl PipelineData {
for s in stream { for s in stream {
let s_live = s?; let s_live = s?;
let bin_output = s_live.as_binary()?; let bin_output = s_live.as_binary()?;
stdout_write_all_as_binary_and_flush(bin_output)?
if !to_stderr {
stdout_write_all_and_flush(bin_output)?
} else {
stderr_write_all_and_flush(bin_output)?
}
} }
} }
@ -472,7 +482,11 @@ impl PipelineData {
out.push('\n'); out.push('\n');
} }
stdout_write_all_and_flush(out)? if !to_stderr {
stdout_write_all_and_flush(out)?
} else {
stderr_write_all_and_flush(out)?
}
} }
} }
None => { None => {
@ -491,7 +505,11 @@ impl PipelineData {
out.push('\n'); out.push('\n');
} }
stdout_write_all_and_flush(out)? if !to_stderr {
stdout_write_all_and_flush(out)?
} else {
stderr_write_all_and_flush(out)?
}
} }
} }
}; };

View file

@ -1,5 +1,3 @@
pub mod utils; pub mod utils;
pub use utils::{ pub use utils::{enable_vt_processing, stderr_write_all_and_flush, stdout_write_all_and_flush};
enable_vt_processing, stdout_write_all_and_flush, stdout_write_all_as_binary_and_flush,
};

View file

@ -24,9 +24,12 @@ pub fn enable_vt_processing() -> Result<()> {
Ok(()) Ok(())
} }
pub fn stdout_write_all_and_flush(output: String) -> Result<()> { pub fn stdout_write_all_and_flush<T>(output: T) -> Result<()>
where
T: AsRef<[u8]>,
{
let stdout = std::io::stdout(); let stdout = std::io::stdout();
let ret = match stdout.lock().write_all(output.as_bytes()) { let ret = match stdout.lock().write_all(output.as_ref()) {
Ok(_) => Ok(stdout.lock().flush()?), Ok(_) => Ok(stdout.lock().flush()?),
Err(err) => Err(err), Err(err) => Err(err),
}; };
@ -34,10 +37,13 @@ pub fn stdout_write_all_and_flush(output: String) -> Result<()> {
ret ret
} }
pub fn stdout_write_all_as_binary_and_flush(output: &[u8]) -> Result<()> { pub fn stderr_write_all_and_flush<T>(output: T) -> Result<()>
let stdout = std::io::stdout(); where
let ret = match stdout.lock().write_all(output) { T: AsRef<[u8]>,
Ok(_) => Ok(stdout.lock().flush()?), {
let stderr = std::io::stderr();
let ret = match stderr.lock().write_all(output.as_ref()) {
Ok(_) => Ok(stderr.lock().flush()?),
Err(err) => Err(err), Err(err) => Err(err),
}; };