Skip to content

join: apply the -e filler to empty output fields - #13718

Merged
cakebaker merged 1 commit into
uutils:mainfrom
dhruv-15-03:join-empty-field-filler
Aug 3, 2026
Merged

join: apply the -e filler to empty output fields#13718
cakebaker merged 1 commit into
uutils:mainfrom
dhruv-15-03:join-empty-field-filler

Conversation

@dhruv-15-03

Copy link
Copy Markdown
Contributor

Fixes #12770

The problem

-e was applied only when a requested output field was absent — that is, when the field index ran past the end of the input line. It was not applied when the field was present but zero length. POSIX specifies -e as replacing "empty output fields", and GNU join substitutes in both cases.

A blank line is the case from the issue: under whitespace splitting it produces a single empty field rather than no fields at all, so the join key was an empty string and the filler was never reached.

Measured against GNU coreutils 8.32 on main (b13ee7a), with a containing hello followed by two blank lines and gap containing a,,b:

command GNU uutils before
join -e X a a hello, X, X, X, X hello followed by four blank lines
join -t, -e X -o 0,1.1,1.2,1.3 gap gap a,a,X,b a,a,,b
join -t, -e X gap gap a,X,b,X,b a,,b,,b

The change

Adds Repr::field_or_empty, which yields the -e filler for a field that is either missing or empty, and routes the three output paths through it:

  • write_field — the join key in the default output format
  • write_fields — the remaining fields in the default output format. This one never consulted the filler at all, so -e had no effect on non-key fields; the change also removes an .unwrap()
  • write_format — the -o path, including -o auto

The filler defaults to the empty string, so output is unchanged when -e is not given, and -e '' behaves the same as omitting the flag. All 35 existing join tests pass untouched, including the three that already exercise -e.

I first suspected WhitespaceSep::field_ranges of manufacturing a spurious trailing empty field, but GNU reports the same field counts there — join -e X on the single line a b (trailing space) prints a b X b X — so the splitter is correct and is left alone.

Verification

Ubuntu 22.04, rustc 1.97.1. Expected behaviour was established by running the GNU binary as a black box and from the POSIX wording for -e; no GNU source was consulted.

  • 21 flag/input combinations diffed against GNU join, comparing stdout, stderr and exit status. 7 differed before the change, 0 differ after.
  • cargo test --no-default-features --features join --test tests -- test_join — 37 passed, 0 failed (35 pre-existing plus 2 new).
  • cargo clippy --no-default-features --features join --bin coreutils --all-targets -- -D warnings — clean.
  • cargo fmt --all -- --check — clean.

I did not run the GNU upstream test suite locally, so I am not claiming anything about its results.

Copilot AI review requested due to automatic review settings August 2, 2026 18:03

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes join’s -e (“empty filler”) behavior so it applies not only when an output field is missing, but also when the field exists and is zero-length (e.g., blank lines, adjacent separators), matching POSIX wording and GNU join.

Changes:

  • Add Repr::field_or_empty and route default output and -o formatting through it so empty fields receive the -e filler.
  • Remove an unwrap() in default-field output while applying the filler consistently.
  • Add regression tests covering blank/whitespace-only lines and adjacent-separator empty fields, including -e "" equivalence.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
src/uu/join/src/join.rs Centralizes “field vs empty filler” resolution and applies it across default and -o output paths.
tests/by-util/test_join.rs Adds tests ensuring -e is applied to zero-length fields and that -e "" preserves empty output.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/uu/join/src/join.rs Outdated
Comment on lines +224 to +227
/// The `--empty` filler stands in for output fields that are empty, which
/// covers both fields missing from the input line and fields that are
/// present but zero length. When `--empty` is not given the filler is
/// itself empty, so this leaves the output unchanged.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct, thanks — there is no --empty long option. Arg::new(e) only sets .short('e'), and GNU doesn't accept one either (join --empty=X gives join: unknown option -- empty; its --help lists just -e EMPTY). Reworded the doc comment to say -e.

GNU join replaces output fields that are empty as well as fields that are
missing from the input, which is how POSIX describes -e ("replace empty
output fields"). uutils only substituted when the requested field index ran
past the end of the line, so a field that was present but zero length was
printed as nothing.

A blank line is the common case: under whitespace splitting it yields a
single empty field rather than no fields at all, so `join -e X a a` printed
blank lines where GNU prints the filler.

Route the three output paths in Repr through a helper that treats an empty
field the same as a missing one. The filler defaults to the empty string, so
output is unchanged when -e is not given.

Fixes uutils#12770

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@dhruv-15-03
dhruv-15-03 force-pushed the join-empty-field-filler branch from 696d982 to ccf50b8 Compare August 2, 2026 18:13
@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown

GNU testsuite comparison:

Skipping an intermittent issue tests/cut/bounded-memory (passes in this run but fails in the 'main' branch)
Skipping an intermittent issue tests/tail/tail-n0f (passes in this run but fails in the 'main' branch)

@cakebaker
cakebaker merged commit c477de7 into uutils:main Aug 3, 2026
165 of 166 checks passed
@cakebaker

Copy link
Copy Markdown
Contributor

Thanks for your PR!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug(join): -e argument fully ignored in the uutils join

3 participants