diff --git a/src/uu/tr/src/tr.rs b/src/uu/tr/src/tr.rs index defe575af06..867d99755b1 100644 --- a/src/uu/tr/src/tr.rs +++ b/src/uu/tr/src/tr.rs @@ -98,11 +98,24 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { } if let Some(first) = sets.first() { - if let Some(b'\\') = os_str_as_bytes(first)?.last() { - show!(USimpleError::new( - 0, - "warning: an unescaped backslash at end of string is not portable" - )); + let slice = os_str_as_bytes(first)?; + + let mut iter = slice.iter(); + + if let Some(b'\\') = iter.next_back() { + match iter.next_back() { + Some(b'\\') => { + // The trailing backslash has a backslash preceding it, so it is properly escaped + } + _ => { + // The trailing backslash has a non-backslash character before it OR is the only character in the + // string, so the warning applies + show!(USimpleError::new( + 0, + "warning: an unescaped backslash at end of string is not portable" + )); + } + } } } diff --git a/tests/by-util/test_tr.rs b/tests/by-util/test_tr.rs index 7445b1dc559..c8f94d9eb14 100644 --- a/tests/by-util/test_tr.rs +++ b/tests/by-util/test_tr.rs @@ -1445,3 +1445,26 @@ fn test_truncate_non_utf8_set() { .succeeds() .stdout_is_bytes(b"\x010mp12"); } + +#[test] +#[cfg(unix)] +fn test_unescaped_backslash_warning_false_positive() { + // Was erroneously printing this warning (even though the backslash was escaped): + // "tr: warning: an unescaped backslash at end of string is not portable" + new_ucmd!() + .args(&["-d", r"\\"]) + .pipe_in(r"a\b\c\") + .succeeds() + .stdout_only("abc"); +} + +#[test] +#[cfg(unix)] +fn test_trailing_backslash_is_only_input_character() { + new_ucmd!() + .args(&["-d", r"\"]) + .pipe_in(r"a\b\c\") + .succeeds() + .stderr_is("tr: warning: an unescaped backslash at end of string is not portable\n") + .stdout_is("abc"); +}