Repository navigation
tr: accept -u for unbuffered output, as GNU, FreeBSD and macOS tr do - #15228
Conversation
FreeBSD and macOS tr take -u to guarantee that output is unbuffered, and scripts written for them use it to pass progress output through a pipeline as it arrives, e.g. `curl ... 2>&1 | tr -u '\r' '\n'`. uutils tr rejected the option, so such pipelines failed outright. On Unix every chunk is already written as soon as it is translated, so -u has nothing left to do there. Windows still writes through the line buffered Stdout, so flush it after every chunk when -u is given.
|
See #15227 (comment) . We should not add this yet. |
| .overrides_with(options::TRUNCATE_SET1), | ||
| ) | ||
| .arg( | ||
| Arg::new(options::UNBUFFERED) |
There was a problem hiding this comment.
Mask this from manpage by .hide(true). While silently supporting is useful for existing scripts, we should not encourage usage of it, or many people would write scripts incompatible with GNU tr.
There was a problem hiding this comment.
Makes sense, done in 742af96. -u is gone from --help and the man page but still accepted. I kept the short note in docs/src/extensions.md since Devel08 asked for it in the issue, happy to drop it if maintainers would rather leave it fully undocumented.
There was a problem hiding this comment.
We can drop it from extensions.md after GNU supported it.
GNU tr rejects -u, so a script that uses it does not run with GNU tr. Keep accepting the option for scripts written for FreeBSD and macOS, but do not advertise it, the same way GNU tr accepts AIX's -A without listing it.
| Arg::new(options::UNBUFFERED) | ||
| .short('u') | ||
| .hide(true) | ||
| .help(translate!("tr-help-unbuffered")) |
There was a problem hiding this comment.
Is this needed if it is hidden?
There was a problem hiding this comment.
Not for --help or the man page, but uudoc still lists hidden options in the zsh/fish/PowerShell completions and on the generated docs page, so -u shows up there either way. Without the text it would just be a bare -u with no explanation; with it, someone who runs into -u in a macOS script can see what it does. It also means unhiding it later is only removing .hide(true). more -u and uname -p/-i keep their help text while hidden too.
| ## `tr` | ||
|
|
||
| Just like on FreeBSD and macOS, `tr` accepts `-u` to guarantee that any output | ||
| is unbuffered: each chunk is written as soon as it has been read and translated. |
There was a problem hiding this comment.
If unbuffered I/O is actually default of GNU tr, we can just say:
Noop. Existing for FreeBSD and macOS compatibility.
There was a problem hiding this comment.
Oh, I missed comment about Windows.
There was a problem hiding this comment.
For the record, GNU tr isn't unbuffered by default either, it writes through stdio. With GNU tr 9.12, { printf abc; sleep 2; printf 'def\n'; } | tr a-z A-Z | cat only prints ABC after the 2 seconds, while stdbuf -o0 tr prints it right away. On Unix it's only our tr that writes every chunk immediately, since #14897.
This comment was marked as low quality.
This comment was marked as low quality.
Sorry, something went wrong.
| pub const DELETE: &str = "delete"; | ||
| pub const SQUEEZE: &str = "squeeze-repeats"; | ||
| pub const TRUNCATE_SET1: &str = "truncate-set1"; | ||
| pub const UNBUFFERED: &str = "unbuffered"; |
There was a problem hiding this comment.
I think we should drop long option since we need BSD compatibility only.
There was a problem hiding this comment.
There isn't one, "unbuffered" is only the argument ID (like SETS = "sets"), there's no .long(). tr --unbuffered is rejected with unexpected argument '--unbuffered' found, only -u is accepted.
|
I've asked GNU in coreutils/coreutils#374. If they add |
| tr-help-delete = delete characters in SET1, do not translate | ||
| tr-help-squeeze = replace each sequence of a repeated character that is listed in the last specified SET, with a single occurrence of that character | ||
| tr-help-truncate-set1 = first truncate SET1 to length of SET2 | ||
| tr-help-unbuffered = guarantee that any output is unbuffered |
There was a problem hiding this comment.
| tr-help-unbuffered = guarantee that any output is unbuffered | |
| tr-help-unbuffered = do not buffer output |
We should be same with GNU, but we don't need to be same with BSD.
There was a problem hiding this comment.
Should be changed in src/uu/tr/locales/fr-FR.ftl as well
There was a problem hiding this comment.
Done in 9853afb, and updated the French string to match.
Say what -u does instead of reusing the BSD man page wording.
GNU tr now accepts -u as an undocumented option as well (coreutils commit a40b8d1191dd), so it is no longer an extension over GNU.
|
GNU merged it as an undocumented option: https://git.savannah.gnu.org/cgit/coreutils.git/commit/?id=a40b8d1191ddcb77dad838c64acc1491dd6298dd (coreutils/coreutils#374). So |
Closes #15227.
Accept
-uintr, as FreeBSD and macOS do, so that scripts written for them stop failing. The examples are in the issue.Since #14897,
tron Unix writes every chunk to the file descriptor as soon as it is translated, so-uhas nothing to do there. On Windowstrstill writes through the line-bufferedStdout, which holds back whatever follows the last newline, so with-uit now flushes after every chunk. The option is hidden from--helpand the man page, as in GNUtr, which accepts the same undocumented-usince coreutils/coreutils#374.Tests added:
test_unbuffered_flag:-uis accepted and leaves the output unchanged.test_unbuffered_partial_line: writesabcwith no newline and expects it back before sending more input, for translation,-dand-s. It times out on line-buffered output, astrhad in 0.12.0.Checked on Windows 11 26H2 with rustc 1.99.0 (MSVC): the
trsuite passes as on main, the new tests passed in 10 of 10 runs, clippy is clean, andtest_unbuffered_partial_linefails if the flush is removed. On Linux thetrsuite, fmt and clippy pass as well.