diff --git a/src/uu/numfmt/locales/en-US.ftl b/src/uu/numfmt/locales/en-US.ftl index 14141b9cc7f..a2ad787bd2f 100644 --- a/src/uu/numfmt/locales/en-US.ftl +++ b/src/uu/numfmt/locales/en-US.ftl @@ -47,6 +47,7 @@ numfmt-help-padding = pad the output to N characters; positive N will right-alig numfmt-help-header = print (without converting) the first N header lines; N defaults to 1 if not specified numfmt-help-round = use METHOD for rounding when scaling numfmt-help-suffix = print SUFFIX after each formatted number, and accept inputs optionally ending with SUFFIX +numfmt-help-unit-separator = use STRING to separate the number from any unit when printing; by default, no separator is used numfmt-help-invalid = set the failure mode for invalid input numfmt-help-zero-terminated = line delimiter is NUL, not newline diff --git a/src/uu/numfmt/src/format.rs b/src/uu/numfmt/src/format.rs index e091f232079..33dc58bc29d 100644 --- a/src/uu/numfmt/src/format.rs +++ b/src/uu/numfmt/src/format.rs @@ -275,6 +275,7 @@ fn transform_to( opts: &TransformOptions, round_method: RoundMethod, precision: usize, + unit_separator: &str, ) -> Result { let (i2, s) = consider_suffix(s, &opts.to, round_method, precision)?; let i2 = i2 / (opts.to_unit as f64); @@ -286,10 +287,15 @@ fn transform_to( ) } Some(s) if precision > 0 => { - format!("{i2:.precision$}{}", DisplayableSuffix(s, opts.to),) + format!( + "{i2:.precision$}{unit_separator}{}", + DisplayableSuffix(s, opts.to), + ) + } + Some(s) if i2.abs() < 10.0 => { + format!("{i2:.1}{unit_separator}{}", DisplayableSuffix(s, opts.to)) } - Some(s) if i2.abs() < 10.0 => format!("{i2:.1}{}", DisplayableSuffix(s, opts.to)), - Some(s) => format!("{i2:.0}{}", DisplayableSuffix(s, opts.to)), + Some(s) => format!("{i2:.0}{unit_separator}{}", DisplayableSuffix(s, opts.to)), }) } @@ -317,6 +323,7 @@ fn format_string( &options.transform, options.round, precision, + &options.unit_separator, )?; // bring back the suffix before applying padding diff --git a/src/uu/numfmt/src/numfmt.rs b/src/uu/numfmt/src/numfmt.rs index abeaca25698..81e8ab9cd0a 100644 --- a/src/uu/numfmt/src/numfmt.rs +++ b/src/uu/numfmt/src/numfmt.rs @@ -234,6 +234,11 @@ fn parse_options(args: &ArgMatches) -> Result { let suffix = args.get_one::(SUFFIX).cloned(); + let unit_separator = args + .get_one::(UNIT_SEPARATOR) + .cloned() + .unwrap_or_default(); + let invalid = InvalidModes::from_str(args.get_one::(INVALID).unwrap()).unwrap(); let zero_terminated = args.get_flag(ZERO_TERMINATED); @@ -246,6 +251,7 @@ fn parse_options(args: &ArgMatches) -> Result { delimiter, round, suffix, + unit_separator, format, invalid, zero_terminated, @@ -370,6 +376,12 @@ pub fn uu_app() -> Command { .help(translate!("numfmt-help-suffix")) .value_name("SUFFIX"), ) + .arg( + Arg::new(UNIT_SEPARATOR) + .long(UNIT_SEPARATOR) + .help(translate!("numfmt-help-unit-separator")) + .value_name("STRING"), + ) .arg( Arg::new(INVALID) .long(INVALID) @@ -419,6 +431,7 @@ mod tests { delimiter: None, round: RoundMethod::Nearest, suffix: None, + unit_separator: String::new(), format: FormatOptions::default(), invalid: InvalidModes::Abort, zero_terminated: false, diff --git a/src/uu/numfmt/src/options.rs b/src/uu/numfmt/src/options.rs index 48f4a4dae0c..eaf0d8b8b48 100644 --- a/src/uu/numfmt/src/options.rs +++ b/src/uu/numfmt/src/options.rs @@ -27,6 +27,7 @@ pub const TO: &str = "to"; pub const TO_DEFAULT: &str = "none"; pub const TO_UNIT: &str = "to-unit"; pub const TO_UNIT_DEFAULT: &str = "1"; +pub const UNIT_SEPARATOR: &str = "unit-separator"; pub const ZERO_TERMINATED: &str = "zero-terminated"; pub struct TransformOptions { @@ -52,6 +53,7 @@ pub struct NumfmtOptions { pub delimiter: Option, pub round: RoundMethod, pub suffix: Option, + pub unit_separator: String, pub format: FormatOptions, pub invalid: InvalidModes, pub zero_terminated: bool, diff --git a/tests/by-util/test_numfmt.rs b/tests/by-util/test_numfmt.rs index d947833f7c7..610e84adb52 100644 --- a/tests/by-util/test_numfmt.rs +++ b/tests/by-util/test_numfmt.rs @@ -1115,3 +1115,16 @@ fn test_zero_terminated_embedded_newline() { // Newlines get replaced by a single space .stdout_is("1000 2000\x003000 4000\x00"); } + +#[test] +fn test_unit_separator() { + for (args, expected) in [ + (&["--to=si", "--unit-separator= ", "1000"][..], "1.0 k\n"), + (&["--to=iec", "--unit-separator= ", "1024"], "1.0 K\n"), + (&["--to=iec-i", "--unit-separator= ", "2048"], "2.0 Ki\n"), + (&["--to=si", "--unit-separator=__", "1000"], "1.0__k\n"), + (&["--to=si", "--unit-separator= ", "500"], "500\n"), // no unit = no separator + ] { + new_ucmd!().args(args).succeeds().stdout_only(expected); + } +}