Skip to content

fix: preserve whitespace in positional arguments - #2423

Merged
dearchap merged 1 commit into
urfave:mainfrom
lenamonj:fix-positional-whitespace
Sep 4, 2026
Merged

dearchap merged 1 commit into
urfave:mainfrom
lenamonj:fix-positional-whitespace

Conversation

@lenamonj

Copy link
Copy Markdown
Contributor

parseFlags computes firstArg := strings.TrimSpace(rargs[0]) to classify each argument, which is correct - but the positional branch then stores the trimmed copy instead of the original argument, so any positional carrying deliberate whitespace is silently rewritten:

cmd := &cli.Command{Name: "prog", Action: ...}
cmd.Run(ctx, []string{"prog", "  padded  "})
// cmd.Args().Slice() == ["padded"], not ["  padded  "]

A filename with a leading space, a message string, a pattern argument - all reach the action modified, with no error. The empty-string branch a few lines above already appends rargs[0] untrimmed, so this change makes the non-empty branch consistent with its sibling: classify with the trimmed copy, append the original.

One line changed in command_parse.go, plus a regression test.

Note: the lone-- branch nearby has the same trimmed append, but that line is already being rewritten by #2419 (the bare-dash fix), so this PR deliberately does not touch it.

@lenamonj
lenamonj requested a review from a team as a code owner August 30, 2026 23:17
@abitrolly

Copy link
Copy Markdown
Contributor

@lenamonj can you give the usage example of the real CLI interface that you are trying to fix?

Shell doesn't allow me to enter tab into CLI, needless to say it won't split it.

@avorima

avorima commented Aug 31, 2026

Copy link
Copy Markdown

@abitrolly It works by quoting the input

./prog " quoted string with spaces "

I personally use this for curl, for example when providing some ad-hoc JSON

curl ... -d '{
// paste multiple lines
// of JSON
}
' # close quoted string and send to curl with whitespace intact

@lenamonj

Copy link
Copy Markdown
Contributor Author

Sure - quoting is how the whitespace gets in, as @avorima says. The case
that bit me is a filename that legally contains a leading or trailing space:

touch " notes.txt"
mytool cat " notes.txt"

Before this change the action receives "notes.txt" with the space stripped, so the open fails with a confusing "no such file" for a file that plainly exists. Same shape for any argument where whitespace is content rather than separator - a search pattern of two spaces, or a message string built by a
calling script:

mytool search "  "     # looks for two spaces, receives ""

The parser is right to trim when classifying the token (deciding flag vs
positional); the bug is that it then stored the trimmed copy instead of the
original.

@lenamonj

lenamonj commented Sep 4, 2026

Copy link
Copy Markdown
Contributor Author

Anything else needed from my side to land this? Happy to rebase if that helps.

@abitrolly

Copy link
Copy Markdown
Contributor

@lenamonj thanks for the use case. It looks like it may cause minor break for users who didn't expect to strip spaces manually. I wish I had a counter example, but I don't.

You say parser detect flags inside quoted strings. I wonder what is the current beaviour for these (sorry, no coding environment ATM).

mytool cat " --notes.txt"
mytool cat "--notes.txt"
mytool cat "--file notes.txt"
mytool cat "  --file notes.txt"

And what is the expected and what would be after the patch..

@lenamonj

lenamonj commented Sep 4, 2026

Copy link
Copy Markdown
Contributor Author

Ran all four against main (1a4deb4) and this branch (93595d0), with a cat subcommand that has a --file flag and prints what it gets:

input main this branch
" --notes.txt" flag provided but not defined: -notes.txt same
"--notes.txt" flag provided but not defined: -notes.txt same
"--file notes.txt" flag provided but not defined: -file notes.txt same
" --file notes.txt" flag provided but not defined: -file notes.txt same
" notes.txt" args=["notes.txt"] args=[" notes.txt"]

Flag detection is unchanged: the parser still trims the token before deciding whether it is a flag, so a quoted string that looks like a flag is treated as one both before and after. The only difference is the last row, where a positional argument now reaches the action as typed instead of trimmed.

On the break risk: anyone relying on the trim was relying on undocumented behaviour, and the fix is strings.TrimSpace at the call site. But it is your call, and I am fine adding a release note if you want one.

@dearchap
dearchap merged commit 5081218 into urfave:main Sep 4, 2026
9 checks passed
@lenamonj
lenamonj deleted the fix-positional-whitespace branch September 5, 2026 12:54
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.

4 participants