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",
Some('n'),
)
.switch("stderr", "print to stderr instead of stdout", Some('e'))
.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> {
let args: Vec<Value> = call.rest(engine_state, stack, 0)?;
let no_newline = call.has_flag("no-newline");
let to_stderr = call.has_flag("stderr");
let head = call.head;
for arg in args {
arg.into_pipeline_data()
.print(engine_state, stack, no_newline)?;
.print(engine_state, stack, no_newline, to_stderr)?;
}
Ok(PipelineData::new(head))

View File

@ -247,7 +247,7 @@ pub fn eval_source(
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);
report_error(&working_set, &err);

View File

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

View File

@ -44,6 +44,7 @@ mod open;
mod parse;
mod path;
mod prepend;
mod print;
#[cfg(feature = "database")]
mod query;
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},
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};
/// 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(
self,
engine_state: &EngineState,
stack: &mut Stack,
no_newline: bool,
to_stderr: bool,
) -> Result<(), ShellError> {
// 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
@ -436,7 +441,12 @@ impl PipelineData {
for s in stream {
let s_live = s?;
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');
}
if !to_stderr {
stdout_write_all_and_flush(out)?
} else {
stderr_write_all_and_flush(out)?
}
}
}
None => {
@ -491,7 +505,11 @@ impl PipelineData {
out.push('\n');
}
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 use utils::{
enable_vt_processing, stdout_write_all_and_flush, stdout_write_all_as_binary_and_flush,
};
pub use utils::{enable_vt_processing, stderr_write_all_and_flush, stdout_write_all_and_flush};

View File

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