xargs: don't emit an empty argument for a trailing blank - #829
Open
kevinburke wants to merge 1 commit into
Open
Conversation
When input ended with a blank immediately before its final newline,
xargs passed one extra empty argument to the command:
$ printf 'aaa \nbbb \n' | xargs printf '[%s]'
[aaa][bbb][] # was
[aaa][bbb] # GNU findutils 4.10.0, and now
WhitespaceDelimitedArgumentReader::next used one `result.is_empty()`
check to answer two different questions. The whitespace branch broke out
of the loop only if `result` was non-empty, so a delimiter seen before
any token was skipped -- correct for blanks, but it left "we are
mid-token" indistinguishable from "we have not started one". The EOF
branch then used `i == 0` as a proxy for "nothing was consumed", which
is false for a final call that consumes only the trailing newline, so it
flushed a zero-length token. Input ending without a newline happened to
work because nothing was left to consume.
Track that state explicitly with an `in_argument` flag, mirroring
`seen_arg` in GNU's read_line, and test `result.is_empty()` at EOF. A
delimiter now ends an argument only when we are inside one, and EOF
flushes only a non-empty buffer.
This also fixes a second symptom of the same conflation: quoted empty
arguments were dropped mid-stream, since quotes start an argument
without contributing any bytes. `printf '"" x\n'` gave [x] and now gives
[][x], again matching GNU. An unterminated `x ''` at end of input still
yields just [x], as GNU's `if (p == linebuf) return -1` does.
-I/-i is unaffected: with a replace string and no explicit delimiter,
xargs uses the newline-delimited reader instead.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Commit bd7e3fe has test result changes: GNU findutils testsuite: |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #829 +/- ##
==========================================
+ Coverage 91.93% 91.99% +0.05%
==========================================
Files 35 35
Lines 7253 7304 +51
Branches 378 378
==========================================
+ Hits 6668 6719 +51
Misses 443 443
Partials 142 142 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
When input ended with a blank immediately before its final newline, xargs passed one extra empty argument to the command:
WhitespaceDelimitedArgumentReader::next used one
result.is_empty()check to answer two different questions. The whitespace branch broke out of the loop only ifresultwas non-empty, so a delimiter seen before any token was skipped -- correct for blanks, but it left "we are mid-token" indistinguishable from "we have not started one". The EOF branch then usedi == 0as a proxy for "nothing was consumed", which is false for a final call that consumes only the trailing newline, so it flushed a zero-length token. Input ending without a newline happened to work because nothing was left to consume.Track that state explicitly with an
in_argumentflag, mirroringseen_argin GNU's read_line, and testresult.is_empty()at EOF. A delimiter now ends an argument only when we are inside one, and EOF flushes only a non-empty buffer.This also fixes a second symptom of the same conflation: quoted empty arguments were dropped mid-stream, since quotes start an argument without contributing any bytes.
printf '"" x\n'gave [x] and now gives [][x], again matching GNU. An unterminatedx ''at end of input still yields just [x], as GNU'sif (p == linebuf) return -1does.-I/-i is unaffected: with a replace string and no explicit delimiter, xargs uses the newline-delimited reader instead.