Merge pull request #2016 from tertsdiepraam/ls/control_characters

ls: show/hide control chars
This commit is contained in:
Sylvestre Ledru 2021-04-04 18:38:15 +02:00 committed by GitHub
commit bd8b129d9a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 197 additions and 38 deletions

View file

@ -119,7 +119,8 @@ pub mod options {
pub static FILE_TYPE: &str = "file-type"; pub static FILE_TYPE: &str = "file-type";
pub static CLASSIFY: &str = "classify"; pub static CLASSIFY: &str = "classify";
} }
pub static HIDE_CONTROL_CHARS: &str = "hide-control-chars";
pub static SHOW_CONTROL_CHARS: &str = "show-control-chars";
pub static WIDTH: &str = "width"; pub static WIDTH: &str = "width";
pub static AUTHOR: &str = "author"; pub static AUTHOR: &str = "author";
pub static NO_GROUP: &str = "no-group"; pub static NO_GROUP: &str = "no-group";
@ -366,24 +367,36 @@ impl Config {
}) })
.or_else(|| termsize::get().map(|s| s.cols)); .or_else(|| termsize::get().map(|s| s.cols));
let show_control = if options.is_present(options::HIDE_CONTROL_CHARS) {
false
} else if options.is_present(options::SHOW_CONTROL_CHARS) {
true
} else {
false // TODO: only if output is a terminal and the program is `ls`
};
let quoting_style = if let Some(style) = options.value_of(options::QUOTING_STYLE) { let quoting_style = if let Some(style) = options.value_of(options::QUOTING_STYLE) {
match style { match style {
"literal" => QuotingStyle::Literal, "literal" => QuotingStyle::Literal { show_control },
"shell" => QuotingStyle::Shell { "shell" => QuotingStyle::Shell {
escape: false, escape: false,
always_quote: false, always_quote: false,
show_control,
}, },
"shell-always" => QuotingStyle::Shell { "shell-always" => QuotingStyle::Shell {
escape: false, escape: false,
always_quote: true, always_quote: true,
show_control,
}, },
"shell-escape" => QuotingStyle::Shell { "shell-escape" => QuotingStyle::Shell {
escape: true, escape: true,
always_quote: false, always_quote: false,
show_control,
}, },
"shell-escape-always" => QuotingStyle::Shell { "shell-escape-always" => QuotingStyle::Shell {
escape: true, escape: true,
always_quote: true, always_quote: true,
show_control,
}, },
"c" => QuotingStyle::C { "c" => QuotingStyle::C {
quotes: quoting_style::Quotes::Double, quotes: quoting_style::Quotes::Double,
@ -394,7 +407,7 @@ impl Config {
_ => unreachable!("Should have been caught by Clap"), _ => unreachable!("Should have been caught by Clap"),
} }
} else if options.is_present(options::quoting::LITERAL) { } else if options.is_present(options::quoting::LITERAL) {
QuotingStyle::Literal QuotingStyle::Literal { show_control }
} else if options.is_present(options::quoting::ESCAPE) { } else if options.is_present(options::quoting::ESCAPE) {
QuotingStyle::C { QuotingStyle::C {
quotes: quoting_style::Quotes::None, quotes: quoting_style::Quotes::None,
@ -408,6 +421,7 @@ impl Config {
QuotingStyle::Shell { QuotingStyle::Shell {
escape: true, escape: true,
always_quote: false, always_quote: false,
show_control,
} }
}; };
@ -619,6 +633,27 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
]) ])
) )
// Control characters
.arg(
Arg::with_name(options::HIDE_CONTROL_CHARS)
.short("q")
.long(options::HIDE_CONTROL_CHARS)
.help("Replace control characters with '?' if they are not escaped.")
.overrides_with_all(&[
options::HIDE_CONTROL_CHARS,
options::SHOW_CONTROL_CHARS,
])
)
.arg(
Arg::with_name(options::SHOW_CONTROL_CHARS)
.long(options::SHOW_CONTROL_CHARS)
.help("Show control characters 'as is' if they are not escaped.")
.overrides_with_all(&[
options::HIDE_CONTROL_CHARS,
options::SHOW_CONTROL_CHARS,
])
)
// Time arguments // Time arguments
.arg( .arg(
Arg::with_name(options::TIME) Arg::with_name(options::TIME)

View file

@ -2,14 +2,22 @@ use std::char::from_digit;
const SPECIAL_SHELL_CHARS: &str = "~`#$&*()\\|[]{};'\"<>?! "; const SPECIAL_SHELL_CHARS: &str = "~`#$&*()\\|[]{};'\"<>?! ";
pub(crate) enum QuotingStyle { pub(super) enum QuotingStyle {
Shell { escape: bool, always_quote: bool }, Shell {
C { quotes: Quotes }, escape: bool,
Literal, always_quote: bool,
show_control: bool,
},
C {
quotes: Quotes,
},
Literal {
show_control: bool,
},
} }
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub(crate) enum Quotes { pub(super) enum Quotes {
None, None,
Single, Single,
Double, Double,
@ -81,6 +89,12 @@ impl EscapeOctal {
} }
impl EscapedChar { impl EscapedChar {
fn new_literal(c: char) -> Self {
Self {
state: EscapeState::Char(c),
}
}
fn new_c(c: char, quotes: Quotes) -> Self { fn new_c(c: char, quotes: Quotes) -> Self {
use EscapeState::*; use EscapeState::*;
let init_state = match c { let init_state = match c {
@ -110,27 +124,10 @@ impl EscapedChar {
Self { state: init_state } Self { state: init_state }
} }
// fn new_shell(c: char, quotes: Quotes) -> Self {
// use EscapeState::*;
// let init_state = match c {
// // If the string is single quoted, the single quote should be escaped
// '\'' => match quotes {
// Quotes::Single => Backslash('\''),
// _ => Char('\''),
// },
// // All control characters should be rendered as ?:
// _ if c.is_ascii_control() => Char('?'),
// // Special shell characters must be escaped:
// _ if SPECIAL_SHELL_CHARS.contains(c) => ForceQuote(c),
// _ => Char(c),
// };
// Self { state: init_state }
// }
fn new_shell(c: char, escape: bool, quotes: Quotes) -> Self { fn new_shell(c: char, escape: bool, quotes: Quotes) -> Self {
use EscapeState::*; use EscapeState::*;
let init_state = match c { let init_state = match c {
_ if !escape && c.is_control() => Char('?'), _ if !escape && c.is_control() => Char(c),
'\x07' => Backslash('a'), '\x07' => Backslash('a'),
'\x08' => Backslash('b'), '\x08' => Backslash('b'),
'\t' => Backslash('t'), '\t' => Backslash('t'),
@ -149,6 +146,15 @@ impl EscapedChar {
}; };
Self { state: init_state } Self { state: init_state }
} }
fn hide_control(self) -> Self {
match self.state {
EscapeState::Char(c) if c.is_control() => Self {
state: EscapeState::Char('?'),
},
_ => self,
}
}
} }
impl Iterator for EscapedChar { impl Iterator for EscapedChar {
@ -170,12 +176,20 @@ impl Iterator for EscapedChar {
} }
} }
fn shell_without_escape(name: String, quotes: Quotes) -> (String, bool) { fn shell_without_escape(name: String, quotes: Quotes, show_control_chars: bool) -> (String, bool) {
let mut must_quote = false; let mut must_quote = false;
let mut escaped_str = String::with_capacity(name.len()); let mut escaped_str = String::with_capacity(name.len());
for c in name.chars() { for c in name.chars() {
let escaped = EscapedChar::new_shell(c, false, quotes); let escaped = {
let ec = EscapedChar::new_shell(c, false, quotes);
if show_control_chars {
ec
} else {
ec.hide_control()
}
};
match escaped.state { match escaped.state {
EscapeState::Backslash('\'') => escaped_str.push_str("'\\''"), EscapeState::Backslash('\'') => escaped_str.push_str("'\\''"),
EscapeState::ForceQuote(x) => { EscapeState::ForceQuote(x) => {
@ -242,7 +256,15 @@ fn shell_with_escape(name: String, quotes: Quotes) -> (String, bool) {
pub(super) fn escape_name(name: String, style: &QuotingStyle) -> String { pub(super) fn escape_name(name: String, style: &QuotingStyle) -> String {
match style { match style {
QuotingStyle::Literal => name, QuotingStyle::Literal { show_control } => {
if !show_control {
name.chars()
.flat_map(|c| EscapedChar::new_literal(c).hide_control())
.collect()
} else {
name
}
}
QuotingStyle::C { quotes } => { QuotingStyle::C { quotes } => {
let escaped_str: String = name let escaped_str: String = name
.chars() .chars()
@ -258,6 +280,7 @@ pub(super) fn escape_name(name: String, style: &QuotingStyle) -> String {
QuotingStyle::Shell { QuotingStyle::Shell {
escape, escape,
always_quote, always_quote,
show_control,
} => { } => {
let (quotes, must_quote) = if name.contains('"') { let (quotes, must_quote) = if name.contains('"') {
(Quotes::Single, true) (Quotes::Single, true)
@ -272,7 +295,7 @@ pub(super) fn escape_name(name: String, style: &QuotingStyle) -> String {
let (escaped_str, contains_quote_chars) = if *escape { let (escaped_str, contains_quote_chars) = if *escape {
shell_with_escape(name, quotes) shell_with_escape(name, quotes)
} else { } else {
shell_without_escape(name, quotes) shell_without_escape(name, quotes, *show_control)
}; };
match (must_quote | contains_quote_chars, quotes) { match (must_quote | contains_quote_chars, quotes) {
@ -289,7 +312,10 @@ mod tests {
use crate::quoting_style::{escape_name, Quotes, QuotingStyle}; use crate::quoting_style::{escape_name, Quotes, QuotingStyle};
fn get_style(s: &str) -> QuotingStyle { fn get_style(s: &str) -> QuotingStyle {
match s { match s {
"literal" => QuotingStyle::Literal, "literal" => QuotingStyle::Literal {
show_control: false,
},
"literal-show" => QuotingStyle::Literal { show_control: true },
"escape" => QuotingStyle::C { "escape" => QuotingStyle::C {
quotes: Quotes::None, quotes: Quotes::None,
}, },
@ -299,18 +325,32 @@ mod tests {
"shell" => QuotingStyle::Shell { "shell" => QuotingStyle::Shell {
escape: false, escape: false,
always_quote: false, always_quote: false,
show_control: false,
},
"shell-show" => QuotingStyle::Shell {
escape: false,
always_quote: false,
show_control: true,
}, },
"shell-always" => QuotingStyle::Shell { "shell-always" => QuotingStyle::Shell {
escape: false, escape: false,
always_quote: true, always_quote: true,
show_control: false,
},
"shell-always-show" => QuotingStyle::Shell {
escape: false,
always_quote: true,
show_control: true,
}, },
"shell-escape" => QuotingStyle::Shell { "shell-escape" => QuotingStyle::Shell {
escape: true, escape: true,
always_quote: false, always_quote: false,
show_control: false,
}, },
"shell-escape-always" => QuotingStyle::Shell { "shell-escape-always" => QuotingStyle::Shell {
escape: true, escape: true,
always_quote: true, always_quote: true,
show_control: false,
}, },
_ => panic!("Invalid name!"), _ => panic!("Invalid name!"),
} }
@ -333,10 +373,13 @@ mod tests {
"one_two", "one_two",
vec![ vec![
("one_two", "literal"), ("one_two", "literal"),
("one_two", "literal-show"),
("one_two", "escape"), ("one_two", "escape"),
("\"one_two\"", "c"), ("\"one_two\"", "c"),
("one_two", "shell"), ("one_two", "shell"),
("one_two", "shell-show"),
("\'one_two\'", "shell-always"), ("\'one_two\'", "shell-always"),
("\'one_two\'", "shell-always-show"),
("one_two", "shell-escape"), ("one_two", "shell-escape"),
("\'one_two\'", "shell-escape-always"), ("\'one_two\'", "shell-escape-always"),
], ],
@ -349,10 +392,13 @@ mod tests {
"one two", "one two",
vec![ vec![
("one two", "literal"), ("one two", "literal"),
("one two", "literal-show"),
("one\\ two", "escape"), ("one\\ two", "escape"),
("\"one two\"", "c"), ("\"one two\"", "c"),
("\'one two\'", "shell"), ("\'one two\'", "shell"),
("\'one two\'", "shell-show"),
("\'one two\'", "shell-always"), ("\'one two\'", "shell-always"),
("\'one two\'", "shell-always-show"),
("\'one two\'", "shell-escape"), ("\'one two\'", "shell-escape"),
("\'one two\'", "shell-escape-always"), ("\'one two\'", "shell-escape-always"),
], ],
@ -362,10 +408,13 @@ mod tests {
" one", " one",
vec![ vec![
(" one", "literal"), (" one", "literal"),
(" one", "literal-show"),
("\\ one", "escape"), ("\\ one", "escape"),
("\" one\"", "c"), ("\" one\"", "c"),
("' one'", "shell"), ("' one'", "shell"),
("' one'", "shell-show"),
("' one'", "shell-always"), ("' one'", "shell-always"),
("' one'", "shell-always-show"),
("' one'", "shell-escape"), ("' one'", "shell-escape"),
("' one'", "shell-escape-always"), ("' one'", "shell-escape-always"),
], ],
@ -379,10 +428,13 @@ mod tests {
"one\"two", "one\"two",
vec![ vec![
("one\"two", "literal"), ("one\"two", "literal"),
("one\"two", "literal-show"),
("one\"two", "escape"), ("one\"two", "escape"),
("\"one\\\"two\"", "c"), ("\"one\\\"two\"", "c"),
("'one\"two'", "shell"), ("'one\"two'", "shell"),
("'one\"two'", "shell-show"),
("'one\"two'", "shell-always"), ("'one\"two'", "shell-always"),
("'one\"two'", "shell-always-show"),
("'one\"two'", "shell-escape"), ("'one\"two'", "shell-escape"),
("'one\"two'", "shell-escape-always"), ("'one\"two'", "shell-escape-always"),
], ],
@ -393,10 +445,13 @@ mod tests {
"one\'two", "one\'two",
vec![ vec![
("one'two", "literal"), ("one'two", "literal"),
("one'two", "literal-show"),
("one'two", "escape"), ("one'two", "escape"),
("\"one'two\"", "c"), ("\"one'two\"", "c"),
("\"one'two\"", "shell"), ("\"one'two\"", "shell"),
("\"one'two\"", "shell-show"),
("\"one'two\"", "shell-always"), ("\"one'two\"", "shell-always"),
("\"one'two\"", "shell-always-show"),
("\"one'two\"", "shell-escape"), ("\"one'two\"", "shell-escape"),
("\"one'two\"", "shell-escape-always"), ("\"one'two\"", "shell-escape-always"),
], ],
@ -407,10 +462,13 @@ mod tests {
"one'two\"three", "one'two\"three",
vec![ vec![
("one'two\"three", "literal"), ("one'two\"three", "literal"),
("one'two\"three", "literal-show"),
("one'two\"three", "escape"), ("one'two\"three", "escape"),
("\"one'two\\\"three\"", "c"), ("\"one'two\\\"three\"", "c"),
("'one'\\''two\"three'", "shell"), ("'one'\\''two\"three'", "shell"),
("'one'\\''two\"three'", "shell-show"),
("'one'\\''two\"three'", "shell-always"), ("'one'\\''two\"three'", "shell-always"),
("'one'\\''two\"three'", "shell-always-show"),
("'one'\\''two\"three'", "shell-escape"), ("'one'\\''two\"three'", "shell-escape"),
("'one'\\''two\"three'", "shell-escape-always"), ("'one'\\''two\"three'", "shell-escape-always"),
], ],
@ -421,10 +479,13 @@ mod tests {
"one''two\"\"three", "one''two\"\"three",
vec![ vec![
("one''two\"\"three", "literal"), ("one''two\"\"three", "literal"),
("one''two\"\"three", "literal-show"),
("one''two\"\"three", "escape"), ("one''two\"\"three", "escape"),
("\"one''two\\\"\\\"three\"", "c"), ("\"one''two\\\"\\\"three\"", "c"),
("'one'\\'''\\''two\"\"three'", "shell"), ("'one'\\'''\\''two\"\"three'", "shell"),
("'one'\\'''\\''two\"\"three'", "shell-show"),
("'one'\\'''\\''two\"\"three'", "shell-always"), ("'one'\\'''\\''two\"\"three'", "shell-always"),
("'one'\\'''\\''two\"\"three'", "shell-always-show"),
("'one'\\'''\\''two\"\"three'", "shell-escape"), ("'one'\\'''\\''two\"\"three'", "shell-escape"),
("'one'\\'''\\''two\"\"three'", "shell-escape-always"), ("'one'\\'''\\''two\"\"three'", "shell-escape-always"),
], ],
@ -437,11 +498,14 @@ mod tests {
check_names( check_names(
"one\ntwo", "one\ntwo",
vec![ vec![
("one\ntwo", "literal"), ("one?two", "literal"),
("one\ntwo", "literal-show"),
("one\\ntwo", "escape"), ("one\\ntwo", "escape"),
("\"one\\ntwo\"", "c"), ("\"one\\ntwo\"", "c"),
("one?two", "shell"), ("one?two", "shell"),
("one\ntwo", "shell-show"),
("'one?two'", "shell-always"), ("'one?two'", "shell-always"),
("'one\ntwo'", "shell-always-show"),
("'one'$'\\n''two'", "shell-escape"), ("'one'$'\\n''two'", "shell-escape"),
("'one'$'\\n''two'", "shell-escape-always"), ("'one'$'\\n''two'", "shell-escape-always"),
], ],
@ -452,9 +516,10 @@ mod tests {
check_names( check_names(
"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F", "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F",
vec![ vec![
("????????????????", "literal"),
( (
"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F", "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F",
"literal", "literal-show",
), ),
( (
"\\000\\001\\002\\003\\004\\005\\006\\a\\b\\t\\n\\v\\f\\r\\016\\017", "\\000\\001\\002\\003\\004\\005\\006\\a\\b\\t\\n\\v\\f\\r\\016\\017",
@ -465,7 +530,15 @@ mod tests {
"c", "c",
), ),
("????????????????", "shell"), ("????????????????", "shell"),
(
"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F",
"shell-show",
),
("'????????????????'", "shell-always"), ("'????????????????'", "shell-always"),
(
"'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F'",
"shell-always-show",
),
( (
"''$'\\000\\001\\002\\003\\004\\005\\006\\a\\b\\t\\n\\v\\f\\r\\016\\017'", "''$'\\000\\001\\002\\003\\004\\005\\006\\a\\b\\t\\n\\v\\f\\r\\016\\017'",
"shell-escape", "shell-escape",
@ -481,9 +554,10 @@ mod tests {
check_names( check_names(
"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F", "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F",
vec![ vec![
("????????????????", "literal"),
( (
"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F", "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F",
"literal", "literal-show",
), ),
( (
"\\020\\021\\022\\023\\024\\025\\026\\027\\030\\031\\032\\033\\034\\035\\036\\037", "\\020\\021\\022\\023\\024\\025\\026\\027\\030\\031\\032\\033\\034\\035\\036\\037",
@ -494,7 +568,15 @@ mod tests {
"c", "c",
), ),
("????????????????", "shell"), ("????????????????", "shell"),
(
"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F",
"shell-show",
),
("'????????????????'", "shell-always"), ("'????????????????'", "shell-always"),
(
"'\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F'",
"shell-always-show",
),
( (
"''$'\\020\\021\\022\\023\\024\\025\\026\\027\\030\\031\\032\\033\\034\\035\\036\\037'", "''$'\\020\\021\\022\\023\\024\\025\\026\\027\\030\\031\\032\\033\\034\\035\\036\\037'",
"shell-escape", "shell-escape",
@ -510,11 +592,14 @@ mod tests {
check_names( check_names(
"\x7F", "\x7F",
vec![ vec![
("\x7F", "literal"), ("?", "literal"),
("\x7F", "literal-show"),
("\\177", "escape"), ("\\177", "escape"),
("\"\\177\"", "c"), ("\"\\177\"", "c"),
("?", "shell"), ("?", "shell"),
("\x7F", "shell-show"),
("'?'", "shell-always"), ("'?'", "shell-always"),
("'\x7F'", "shell-always-show"),
("''$'\\177'", "shell-escape"), ("''$'\\177'", "shell-escape"),
("''$'\\177'", "shell-escape-always"), ("''$'\\177'", "shell-escape-always"),
], ],
@ -530,10 +615,13 @@ mod tests {
"one?two", "one?two",
vec![ vec![
("one?two", "literal"), ("one?two", "literal"),
("one?two", "literal-show"),
("one?two", "escape"), ("one?two", "escape"),
("\"one?two\"", "c"), ("\"one?two\"", "c"),
("'one?two'", "shell"), ("'one?two'", "shell"),
("'one?two'", "shell-show"),
("'one?two'", "shell-always"), ("'one?two'", "shell-always"),
("'one?two'", "shell-always-show"),
("'one?two'", "shell-escape"), ("'one?two'", "shell-escape"),
("'one?two'", "shell-escape-always"), ("'one?two'", "shell-escape-always"),
], ],

View file

@ -1142,9 +1142,9 @@ fn test_ls_quoting_style() {
assert_eq!(result.stdout, "'one'$'\\n''two'\n"); assert_eq!(result.stdout, "'one'$'\\n''two'\n");
for (arg, correct) in &[ for (arg, correct) in &[
("--quoting-style=literal", "one\ntwo"), ("--quoting-style=literal", "one?two"),
("-N", "one\ntwo"), ("-N", "one?two"),
("--literal", "one\ntwo"), ("--literal", "one?two"),
("--quoting-style=c", "\"one\\ntwo\""), ("--quoting-style=c", "\"one\\ntwo\""),
("-Q", "\"one\\ntwo\""), ("-Q", "\"one\\ntwo\""),
("--quote-name", "\"one\\ntwo\""), ("--quote-name", "\"one\\ntwo\""),
@ -1161,6 +1161,42 @@ fn test_ls_quoting_style() {
println!("stdout = {:?}", result.stdout); println!("stdout = {:?}", result.stdout);
assert_eq!(result.stdout, format!("{}\n", correct)); assert_eq!(result.stdout, format!("{}\n", correct));
} }
for (arg, correct) in &[
("--quoting-style=literal", "one?two"),
("-N", "one?two"),
("--literal", "one?two"),
("--quoting-style=shell", "one?two"),
("--quoting-style=shell-always", "'one?two'"),
] {
let result = scene
.ucmd()
.arg(arg)
.arg("--hide-control-chars")
.arg("one\ntwo")
.run();
println!("stderr = {:?}", result.stderr);
println!("stdout = {:?}", result.stdout);
assert_eq!(result.stdout, format!("{}\n", correct));
}
for (arg, correct) in &[
("--quoting-style=literal", "one\ntwo"),
("-N", "one\ntwo"),
("--literal", "one\ntwo"),
("--quoting-style=shell", "one\ntwo"),
("--quoting-style=shell-always", "'one\ntwo'"),
] {
let result = scene
.ucmd()
.arg(arg)
.arg("--show-control-chars")
.arg("one\ntwo")
.run();
println!("stderr = {:?}", result.stderr);
println!("stdout = {:?}", result.stdout);
assert_eq!(result.stdout, format!("{}\n", correct));
}
} }
let result = scene.ucmd().arg("one two").succeeds(); let result = scene.ucmd().arg("one two").succeeds();