Repository navigation
Implement accommodations for Windows CLIs, notably batch files and misexec-style programs as part of experimental feature PSNativeCommandArgumentPassing #15143
Description
Summary of the new feature/enhancement
A Guide to this Proposal:
-
The rest of this initial post details the motivation and the proposed implementation.
-
Easy-to-grasp examples of the proposed accommodations are in this comment.
-
Complementary examples of what won't work unless we implement these accommodations are in another comment.
As requested by Travis Plunk (@TravisEz13) in #14692 (comment), following a suggestion from Ilya (@iSazonov) in #14692 (comment):
The following is adapted from #14747 (comment), which contains some additional information about native argument-passing on Windows.
PR #14692 introduces experimental feature PSNativeCommandArgumentPassing that will address parameter-passing woes when calling native programs with respect to embedded quoting and empty-string arguments, taking advantage of System.Diagnostics.ProcessStartInfo.ArgumentList, which:
-
on Unix-like platforms: fully solves all problems.
-
on Windows: solves the problem only for those programs that adhere to the quoting and escaping conventions used by Microsoft's C/C++ runtime.
While this is a great step in the right direction, it leaves out many Windows CLIs that do not play by these rules:
-
The most prominent exception is
cmd.exe- and therefore calls to batch files: they accept only""as an escaped", not the\"required by the C/C++ convention); while Microsoft compiler-generated executables also support"", there are third-party programs that support only\") -
An additional problem is that batch files unfortunately and inappropriately parse their arguments as if they had been passed from inside
cmd.exe, which causes something like.\foo.cmd http://example.org?foo&barto break due to&being misinterpreted as a statement separator. Using"http://example.org?foo&bar", i.e. quoting from PowerShell doesn't help, because PowerShell - justifiably - omits the quotes when it rebuilds the process command line behind the scenes, given that value contains neither spaces nor embedded"chars.- This is especially problematic given that the CLIs of many high-profile environments (e.g.,
az.cmdfor Azure, and the wrapper batch files thatnpm(Node.js's package manager) creates for (Java)script-based utilities that come with packages) use batch files as their CLI entry points, so that something like az ... 'http://example.org?foo&bar'predictably fails.
- This is especially problematic given that the CLIs of many high-profile environments (e.g.,
-
Calling
cmd.exe /c "<command-line>"orcmd.exe /k "<command-line>"directly with a single-argument command line to be executed through a happy accident actually currently works as intended, without a workaround - and that behavior must be retained. -
Many programs are particular about partial quoting of arguments, notably
msiexec.exewith property arguments such asPROP="VALUE WITH SPACES"; purely syntactically,"PROP=VALUE WITH SPACES"(which is what PowerShell currently sends) should be equivalent (and if you let the C/C++ runtime / CLR parse it, is - the resulting verbatim string isPROP=VALUE WITH SPACESin both cases), but in practice it is not.- PowerShell should not pay attention to the original quoting on the PowerShell command line in an attempt to emulate it when re-encoding behind the scenes; no such quoting may be present to begin with (e.g.,
PROP=$someValuePossiblyWithSpaces), and users generally shouldn't have to worry about such intricacies - see below.
- PowerShell should not pay attention to the original quoting on the PowerShell command line in an attempt to emulate it when re-encoding behind the scenes; no such quoting may be present to begin with (e.g.,
-
Finally, calls to the WSH (Windows Script Host) CLIs
cscript.exe(console) andwscript.exe- either directly or via associated script file types, notably.vbs(VBScript) and.js(JScript), behave poorly with\"-escaped embedded"characters; while the problem cannot be fully solved,""-escaping results in better behavior: see below for details.
It's impossible for PowerShell to fully solve these problems, but it makes sense to make accommodations for these exceptions, as long as they are based on general rules (rather than individual exceptions) that are easy to conceptualize and document.
I believe it is vital to make these accommodations as part of the PSNativeCommandArgumentPassing experimental feature implemented in PR #14692 in order to solve the vast majority of quoting headaches once and for all.
They are detailed below.
For the remaining, edge cases there is:
--%for console applications, or, preferably, because it has fewer limitations and enables use of PowerShell variable values and expressions via string interpolation,cmd /c "<cmd.exe command line>".Start-Processfor GUI-subsystem applications with a CLI such asmsiexec, which allows you to fully control the process command line by passing a single string to-ArgumentList(in a pinch you can also use it with console applications, but you lose stream integration).
Proposed technical implementation details
After PowerShell's own parsing, once the array of verbatim arguments - stripped of $nulls - to pass on is available:
-
On Unix-like platform:
- Pass that array to
.ArgumentList- that is all that is ever needed.
- Pass that array to
-
On Windows:
-
Except for the cases detailed below, also pass that array to
.ArgumentList- behind the scenes; .NET then performs the necessary re-encoding based on the C/C++ conventions for us, and any conventional CLI should interpret the result correctly. -
The following exceptions may apply independently or in combination, and they require manual re-encoding by PowerShell (with assignment to
.Arguments, as currently):-
A current behavior that must be retained - i.e. no escaping of embedded
"must be performed - is the very specific case ofcmd.exebeing called directly, with either the/cor the/koption followed by a single argument (with spaces) representing acmd.execommand line in full.- See here for details, including the proposal for an optional additional accommodation that would make sense, namely to robustly support passing the command line following
/cor/kas multiple arguments, by transforming it into a single-argument, double-quoted-overall form. - Implementation-wise, we'd get this additional accommodation almost for free, because we need it behind the scenes for calling batch files anyway, so as to support reliable exit-code reporting - see next point.
- See here for details, including the proposal for an optional additional accommodation that would make sense, namely to robustly support passing the command line following
-
If the target command is a batch file:
- use
""(rather than\") to escape embedded verbatim"(and ensure enclosure in syntactic"...", even if the value has no spaces) "..."-enclose any argument that contain no spaces (such arguments are normally not quoted) but contain any of the followingcmd.exemetacharacters:& | < > ^ , ;(while,and;have no impact on arguments pass-through with%*, they serve as argument separators in intra-batch file argument parsing; this also applies to=, but, unfortunately, passing something likeFOO=baras"FOO=bar"conflicts with the accommodation formsiexec-style CLIs below).- Additionally, for reliable exit-code reporting, call the batch file via
cmd /c "<batch-file> ... & exit"rather than directly; see below for the detailed rationale.- This means:
- Make
cmd.exethe executable. - Pass a single argument string
/c "<batch-file> ... & exit", where<batch-file>path may need to be double-quoted and...represents the space-joined list of the arguments quoted based on the rules above. Again, no escaping of any"characters ending up in the overall"..."string passed to/cneed or must be performed. - Note that on Windows versions before Windows 10, such a call fails if the batch-file path itself must be double-quoted (typically, due to containing spaces); the workaround is to determine the short (8.3) form of that path - which by definition doesn't require double-quoting - and use that instead.
- Make
- This means:
- use
-
Irrespective of the target executable, if any of the arguments have the form of a
misexec-style partial-quoting argument, apply double-quoting only to the "value" part (the part after the separator):- Specifically, if an argument (a) matches regex
^([/-]\w+[=:]|\w+=)(.+)$, and (b) the part after=or:requires double-quoting (either due to containing spaces and/or an embedded"and/or, in the case of a batch file containingcmd.exemetacharacters), leave the part up to and including=or:unquoted, and double-quote only the remaining part. - Examples:
- The following PowerShell arguments:
FOO='bar none',-foo:$value(with$valuecontaining verbatimbar none),/foo:bar` none(and even quoted-in-full variants'FOO=bar none', ....)
- would end up in the
.Argumentscommand line as follows:FOO="bar none",-foo:"bar none",/foo:"bar none"
- The following PowerShell arguments:
- Specifically, if an argument (a) matches regex
-
If the target executable is a WSH CLI -
cscript.exeorwscript.exe- or the filename extension is one of the following WSH-associated extensions listed by default in$env:PATHEXT(which makes them directly executable):.vbs .vbe .js .jse .wsf .wsh, use""rather than\"to escape"characters embedded in arguments; again see below for details.
-
-
Again, these are reasonable accommodations to make, which:
- allow users to focus solely on PowerShell's syntax
- should make the vast majority of calls just work.
- are easy to conceptualize and document - the proposed tracing should help too.
I invite everyone to scrutinize these accommodations to see if they're complete, overzealous, ...
This is a chance to finally cure all native quoting / argument-passing headaches - even if only by opt-in.
(To experiment with the proposed behaviors up front (based on my personal implementation that sits on top of the current behavior), you can use Install-Module Native and prepend ie to command lines; if the proposed changes are implemented, such a stopgap will no longer be necessary, although it can still help on earlier versions.)
Activity
iSazonov commentedon Apr 2, 2021
I'm delighted that James Truher (@JamesWTruher) implemented #14692!
I hope we will not stop half way and finally close this story by fully implementing the proposal.
mklement0 commentedon Apr 8, 2021
As it turns out, there's another accommodation worth making for batch files with respect to exit codes (now implemented in v1.3.1 of the
Nativemodule):$LASTEXITCODE, reliably; e.g.:.\test.cmd:cmd /c ".\test.cmd & exit", as detailed in this Stack Overflow post.Note that inside a
cmd.exesession,%ERRORLEVEL%is set to1after calling the batch file.To be clear: The problem lies with
cmd.exe, but it is yet another accommodation we can make to improve the robustness of native-program calls.mklement0 commentedon Apr 19, 2021
The following:
PSNativeCommandArgumentPassing: Missing-accommodations examples, current bugs:Setup:
Install-Module NativeNote:
The diagnostic output below, via argument-/command-line diagnosing function
dbea(which uses-ieto use theiefunction behind the scenes, which ensures the desired behavior); these functions are from theNativemodule. It is my hope that the need foriewill go away with proper implementation ofPSNativeCommandArgumentPassing(though it will still be useful in older PS versions).First shows the verbatim arguments that the target batch file / conventional executable ends up seeing after parsing its own command line, i.e. the effective values.
Then shows the raw command line (sans executable) that was passed (which by definition only applies on Windows): this is what PowerShell must construct from the given arguments and assign to
System.Diagnostics.ProcessStartInfo.Arguments.Accommodation A: Calling a batch file: Must escape embedded
"as"":Note how the verbatim embedded
"The Hawk"was escaped as""The Hawk"", to ensure that the batch file properly recognized the entire token as a single argument.Accommodation B: Calling a batch file: Space-less arguments (ones that normally do not trigger enclosure in
"...") must still be double-quoted if any of the followingcmd.exemetacharacters is present:& | < > ^ , ;Note how the URL was double-quoted, despite not containing spaces.
Currently, this fails, both with and without
PSNativeCommandArgumentPassingin effect, because in the absence of double-quotingcmd.exe(which parses the batch file's command line) interprets the unquoted&as a statement separator.Accommodation C: Passing a
PROPERTY=VALUE-style argument (and variations thereof, prefixed with/or-optionally combined with:instead of=) to any executable: Must selectively"..."-enclose only theVALUEpart, if it contains spaces so as to satisfy the particular, nonstandard quoting requirements of executables such asmsiexec(to conventional executables, this quoting variation makes no difference, so it is safe to use):Note how
INSTALLDIR=$PSHOMEturned into selectively double-quotedINSTALLDIR="C:\Program Files\PowerShell\7"Note:
cmd /c "<batch-file> ... & exit"call - see below.Accommodation D: Support reliable exit-code reporting from batch files:
Without
ie, this currently reports0(both with and withoutPSNativeCommandArgumentPassingin effect), i.e.whoami's failure exit code was not reported.For this to work as intended, PowerShell must translate
./test.cmd ...intocmd /c "c:\path\to\test.cmd ... & exit, as explained in detail here. Due tocmd.exenot expecting escaping inside the overall"..."string, the encoding of the arguments is the same as would be needed for Accommodation B in isolation.Accommodation E: Use
""-escaping for WSH calls (direct and indirect calls ofcscript.exe/wscript.exe) to result in less broken behavior:Note how the embedded
"were stripped - which cannot be avoided, unfortunately - but the argument boundaries were preserved, whereas applying the default\"escaping would result both in broken argument boundaries and literally retained\chars - see below for details.Accommodation F: Transform multi-argument direct
cmd /c/cmd /kexecutable calls into single-argument, overall"..."-enclosed ones behind the scenes.This accommodation is arguably the least important, but I think it would be beneficial, because it's one less thing for users to worry about; plus, the important Accommodation D would give us the implementation for free:
Without
ie, this command breaks, both with and withoutPSNativeCommandArgumentPassingin effect, due tocmd.exe's own limitations.If PowerShell transformed the above command line into the following, verbatim single-argument
cmd /cform in.Arguments(which is whatiedoes), the command would work:To simulate this with
--%:For details, see #14747 (comment)
Fix 1: Calling
cmd.exe /c/cmd.exe /kdirectly with a command line (as a single argument) must continue to work properly, as it (by lucky accident) always has, even withPSNativeCommandArgumentPassingin effect:With
PSNativeCommandArgumentPassingin effect ($PSNativeCommandArgumentPassing='Standard'), you'll now get (broken)\"Andre \"\"The Hawk\"\" Dawson\".Such
cmd /ccalls have historically been valuable for working around quoting headaches in PowerShell.They will continue to be useful:
--%, given that embedding PowerShell variable values and expressions is then easily possible via string interpolation.cmd.exe's|.See #15239.
Fix 2:
--%must continue to work even withPSNativeCommandArgumentPassingin effect:--%, the stop-parsing symbol, is a per-call syntax override, and its functioning should not be affected by whether or notPSNativeCommandArgumentPassingin effect. While--%is virtually useless on Unix, on Windows there will still be - hopefully very rare - edge cases that require it, and there's also no need to break existing calls using it.With
PSNativeCommandArgumentPassingin effect ($PSNativeCommandArgumentPassing='Standard'), you'll now get(broken)
"\"Andre" "\"\"The" "Hawk\"\"" "Dawson\""See #15261.
Fix 3: Passing arguments such as
-F:(looks like a PowerShell parameter, ends in:) must continue to work even withPSNativeCommandArgumentPassingin effect:The following fails as of PowerShell Core 7.2.0-preview.5, because the
-F:andfooarguments are unexpectedly merged behind the scenes:See #15276
Fix 4:
PSNativeCommandArgumentPassingmust not break invocation of WSH scripts (VBScripts, JScripts) with arguments.See #15289
mklement0 commentedon Apr 21, 2021
Steve Lee (@SteveL-MSFT), on a meta note similar to the one in #14025 (comment):
Two separate requests to discuss the issue at hand in the April community call were made (one by Ilya (@iSazonov), referring here directly, and by Justin Grote (@JustinGrote), referring here indirectly, via #1995 (comment)).
In the community call you glossed over these requests by saying that James Truher (@JamesWTruher)'s presentation had already covered the topic by his presentation on the new, experimental
PSNativeCommandArgumentPassingfeature.This despite the fact that the very point of the issue at hand is to point out problems with the feature as currently implemented - and that is what needed to be discussed.
That the discussion was simply brushed aside again does not instill confidence in the concerns of the community being taken seriously.
JamesWTruher commentedon Apr 21, 2021
On Windows, there is basic inconsistency between the way a batch file arguments and Windows executable arguments are parsed. with a batch file, the
=seems to be a token separator, but not with the compiled executable. This was most surprising. Here's the transcript (echoit.exe is a very simple native app). All of the following is done from cmd.exe"from PowerShell, the experience is as follows:
I didn't try a VB script.
My concern with this request would be to have PowerShell have to know too much about what is being executed. Right now there is no disambiguation about what is being executed and to change the way things are parsed when running a batch file vs an executable seems very improper to me. With regard to MSIEXEC.EXE specifically, i believe we will handle that just fine. If there is an issue with regard to an .exe, i really need to see the specifics.
mklement0 commentedon Apr 21, 2021
Thanks for engaging, James Truher (@JamesWTruher).
Before we get into specifics:
It's not improper. It's an invaluable accommodation that relieves users of (most of) the burden of having to account for the anarchy of argument-passing on Windows.
Ensuring that arguments are ultimately passed as-is - based on PowerShell's parsing rules alone - to external programs as-is is a core duty of a shell.
While the limitations of Windows prevent a fully robust solution, we can provide a solution that covers most use cases, based on straightforward rules - and the ones laid out for the class of batch files (as opposed to hard-coding exceptions for specific executables) achieve that.
No. passing something like
foo='bar none'results in"foo=bar none"on the behind-the-scenes command line, whichmsiexec.exe(and others, such asmsdeploy.exe) do not recognize.Again, the proposed accommodation is based on an arguments matching a (regex) pattern, not on specific executables.
Good point - I hadn't considered VBScript - I'll investigate.
mklement0 commentedon Apr 21, 2021
Quick update, James Truher (@JamesWTruher):
PSNativeCommandArgumentPassingbreaks invocation of VBScripts with arguments: see #15289mklement0 commentedon Apr 22, 2021
As for VBScript (WSH):
VBScript's command-line argument parsing is provided by the WSH (Windows Script Host) CLIs,
cscript.exe(console) andwscript.exe(GUI), so with direct invocation of script files the same behavior applies to the following filename extensions listed in$env:PATHEXT, which includes both VBScript and JScript scripts and their variants, as well as WSH wrapper files:WSH supports neither
\"- nor""-escaping of embedded"", but using""results in less broken behavior, and is therefore another accommodation worth making - I've updated the initial post and the comment with the examples accordingly:WSH parses a command-line token such as
"Andre ""The Hawk"" Dawson"as 3 directly adjacent"..."substrings, which it implicitly joins to form single, verbatim argumentAndre The Hawk Dawson. While this is broken in that the embedded"were stripped, at least the token was still recognized as a single argument.By contrast,
"Andre \"The Hawk\" Dawson"- which is whatPSNativeCommandArgumentPassingcurrently does (as of this writing you must invoke scripts explicitly withcscript.exe, due to bug #15289) - is parsed as two arguments, and the\characters are _retained: That is,"Andre \"Thebecomes the the first argument, as verbatimAndre \The, andHawk\" Dawson"becomes the second, as verbatimHawk\ Dawson.To demonstrate the difference, assuming the following
test.vbsscript:PSNativeCommandArgumentPassingbehavior:\"-escaping:Note the broken argument partitioning and the literally retained
\chars.""-"escaping"; you needInstall-Module Nativev1.4.1 or higher for theiefunction to show this behavior):While the behavior is partially broken - what were meant to be embedded
"were stripped - at least the argument was still recognized as a single argument and no characters were added.mklement0 commentedon Apr 22, 2021
James Truher (@JamesWTruher), with respect to batch files:
Specifically, unquoted
<space> , ; =interchangeably act as argument separators in batch-file command lines (though in practice, it seems, it is only ever spaces that are used).Note how PowerShell implicitly double-quoted
'a b', due to containing a space, but the other - spaceless ones - were passed unquoted - causing unexpected argument separation. That is, what you clearly meant to pass as verbatima,b, for instance - on the PowerShell side single-quoting was used, after all - should also be received by the batch file as such - that is the job of a shell.With the proposed Accommodation B (see above), even such space-less arguments will be double-quoted in batch-file calls, to ensure that with the exception of
=, and the addition of& | < > ^:Note that arguments
'a&b' 'a|b' 'a<b', 'a>b' 'a^b'simply break the call with the currentPSNativeCommandArgumentPassingimplementation, because the unquoted& | < > ^are then interpreted ascmd.exemetacharacters.The reason for the
=exception is that it conflicts with Accommodation C formsiexec-style executables (where thea=part ofa=bmust be unquoted), and I think the latter should be given precedence in this case.Fortunately, passing
a=bthrough as a single argument as part of relaying the command line via%*isn't affected, as you can see from the output above, and over time I expect more and more batch files to be nothing more than CLI entry points with pass-through arguments (e.g., for Python CLIs, such as theaz.cmdbatch file that is the Azure CLI's entry point).mklement0 commentedon Apr 25, 2021
The initial post details the proposed accommodations and their benefits and this follow-up comment provides examples of what we would gain.
Perhaps it is helpful to complement that with examples of what won't work, unless these accommodations are implemented:
The examples show the current behavior with
PSNativeCommandArgumentPassingin effect, as of PowerShell Core 7.2.0-preview.5; their names correspond to the accommodation examples above; all commands can be made to work properly withInstall-Module Nativeand by prepending each invocation withie(e.g.ie .\temp.cmd ...)Unsupported Scenario A: Inability to pass arguments with embedded
"to batch files:As you can see, not only was the argument not recognized as a whole, the command broke, because - due to the embedded
"being escaped as\"instead of""- the fromcmd.exe's perspective effectively unquoted&was interpreted as a statement separator.Unsupported Scenario B: Inability to pass space-less arguments that contain
& , ; ^ | < >to batch files:Using Azure's
azCLI, which is implemented as wrapper batch fileaz.cmdthat calls a Python script, as an example:Since the URL by definition contains no spaces, PowerShell passes it unquoted, so that the unquoted
&is again interpreted ascmd.exe's _statement separator.In other words: with direct invocation, it is impossible to pass a URL that contains
&character to a batch file.Unsupported Scenario C: Inability to pass
PROPERTY="VALUE WITH SPACES"style arguments tomsiexec-style executables:Because PowerShell passes
"INSTALLDIR=c:\program files\foo"rather than theINSTALLDIR="c:\program files\foo"required bymsiexec- quoting of the value part only -msiexecencounters a syntax error.Unsupported Scenario D: Inability to reliably report a batch file's exit code:
That is, even though
whoamireported exit code1and the batch file was exited as result of that, PowerShell saw exit0, mistakenly implying that the batch file succeeded.Unsupported Scenario E: Inability to call WSH (VBScript, JScript) scripts with arguments with embedded
"while preserving at least argument boundaries:Note how the argument wasn't recognized as a whole, and a
\was inserted, resulting from the\"-escaping that is applied.As detailed above, there is no complete solution here, but using
""-escaping at least preserves argument boundaries (while stripping embedded").Unsupported Scenario F: Inability to call
cmd.exewith/cor/kwith individual arguments if both the executable path and one of the arguments contain":TSlivede commentedon Apr 25, 2021
I don't know if the behavior of scenario F should be changed. This is simply the behavior of cmd.exe. To make it work just use
This form of the command works in Windows Powershell 5.1, it works in the current powershell preview with
PSNativeCommandArgumentPassingenabled and it works in cygwin bash and in ubuntu wsl bash.I'm just not a huge fan of detecting a specific exe file and behaving differently. Detecting a specific file type (
.vbsor.bat) would also not be ideal, but would be IMHO much cleaner then detecting a specific exe.6 remaining items
mklement0 commentedon May 6, 2021
You're confirming Kirk Munro (@KirkMunro)'s point.
The sad thing about this decision is that in this case there is no need to choose between serving favored groups and quality-of-life improvements for the community at large.
Introducing another setting will just create more confusion while providing no benefit at all; on the contrary.
Only two settings are needed:
Legacy- keep everything as is, for those who need to write code targeting both editions on Windows (and possibly older PS Core versions on Unix). The Byzantine workarounds required to work around the broken legacy behavior in that case are then unavoidable.Standard- make PowerShell act like a proper shell as much as possible on Windows, for those free to target v7.2+ only, which requires implementing the proposed accommodations . By contrast, with the (lucky) exception ofcmd /c, the legacy behavior is hopelessly broken, so introducing a selective opt-back-in setting is both confusing and utterly unhelpful.What you call "magic" is the very opposite from the user's perspective:
It enables users to focus on PowerShell's syntax alone, trusting the shell to pass arguments on as specified solely by its own rules - that is a core mandate of a shell. That the act of passing arguments to external programs requires additional work behind the scenes is (a) unfortunate historical baggage on Windows that cannot be avoided and (b) should be an implementation detail that the user is shielded from.
By not providing a single setting that combines
.ArgumentListwith the selective Windows accommodations proposed, you're forever saddling Windows users with quoting headaches and obscure failures.As stated, batch files as CLI entry points are common (Azure, npm-installed script-based executables, ...) and users may not even be aware that they're calling batch files, but - more importantly - they shouldn't have to be.
In the absence of a proper solution, the
iefunction from theNativemodule (Install-Module Native) can be used: it incorporates all proposed accommodations, and equally works in Windows PowerShell v3+ as well as in all PowerShell Core versions, irrespective of the availability of thePSNativeCommandArgumentPassingfeature and its setting; just prependieto your native-executable calls.Perhaps needless to say, needing a third-party module to make PowerShell fulfill one of its core mandates as a shell is a sad state of affairs.
As part of my ongoing withdrawal (in both senses of the word): I've said all that I have to say, and I'm unsubscribing from this thread.
README.md upated
rkeithhill commentedon May 12, 2021
I don't quite get this. There was a lot of resistance to "fixing" argument passing by "special casing" this feature for certain executables. But in the end, the team opts to "special case" the feature for these executables anyway - except not to fix the issues but drop back to the old (legacy) parsing mode. That's a head scratcher.
This new approach feels more like a "Hybrid" mode where some executables get the old parsing treatment. Is that list configurable? What if we discover another native Windows app that doesn't behave well with Standard mode?
Seems like this feature should stay experimental until the issues with cmd.exe, msiexec and c/wscript can be worked out. BTW Michael Klement (@mklement0), thanks for the
Nativemodule. It's quite handy.One last note, the recent loss of community support should be concerning to the team. Something is clearly not working right. Don't get me wrong, I don't think the team should merge community PRs willy / nilly. There needs to be a high quality bar to ensure new PowerShell releases are high quality and avoid breaking changes as much as possible. But Michael Klement (@mklement0) has made some in-depth and well researched suggestions here that appear to be falling on deaf ears. :-(
mklement0 commentedon Jun 18, 2021
The saga continues at #15408 (comment)
vexx32 commentedon Jun 22, 2021
Pretty sure doing that was one of the main premises James Truher (@JamesWTruher) was working from. I'd agree that's probably going to cause more confusion than it helps ultimately, though. 🤔
jimbobmcgee commentedon Feb 16, 2022
There are two things I can think of which might inform some of this, but I yet haven't fully explored...
There is a
/smodifer forcmd /candcmd /kwhich deliberately alters the behaviour of quoted arguments, that you may also need to factor. The wording for exactly how that works incmd /?is somewhat confusing, but I have historically found circumstances (particularly in batch files) where adding/shas made all the difference (although I'd have to rummage to find anything concrete).I only call this out because, since you are reworking quoting for
cmd /c "..."andcmd /k "...", you may also need to rework quoting forcmd /s /c "..."andcmd /s /c "..."to match that behaviour, too.The documentation in
help exitdoes not expressly call out the behaviour you describe in Implement accommodations for Windows CLIs, notably batch files and misexec-style programs as part of experimental feature PSNativeCommandArgumentPassing #15143 (comment), so is it a valid test? As I read it,EXIT [/b] [exitCode]makes both/bandexitCodeoptional, and only says that/bexits a batch script, not the interpreter (which was actually wrong up until approx. Vista). The callexit /bdoes not specify an exit code, only that a batch file should exit. There is no help-documented default exit code ifexitCodeis not passed (I've always assumed0), nor does it expressly state that it passes through any prior errorlevel.As such, in
whoami -nosuch 2>NUL || exit /b, isn't that technically saying ifwhoamiexits with non-zero, exit from the batch, not ifwhoamiexits with non-zero, exit from the batch with the exit code ofwhoami? (the exit code being arbitrary/undefined, and I've always assumed as0).(I've always used
IF NOT "{%ERRORLEVEL%}"=="{0}" EXIT /b %ERRORLEVEL%as my terminate batch with prior exit code approach, but maybe that's never been necessary.)Other things I can think of, which may or may not be relevant...
if we're building a list of exceptional quoting rules,
find.exe '"hello"' hello.txthas always been a pain. It's even worse if you want to interpolate:find.exe "`"hello $myvar`""(even moreso if you want to put that in Markdown!)also consider
forfiles.exe, which has its own expectations for/c(including/c cmd /c "foo") and its own processing of 0xHH hex numbers --forfiles.exe /c "cmd /c 0x22runme /x @fpath0x22".there is also special handling of
^which can act, in some cases, as an escape character for Windows batch and at least one of the Win32/ShellExecute command-line building args.microsoft-github-policy-service commentedon Nov 23, 2023
This issue has been marked as "No Activity" as there has been no activity for 6 months. It has been closed for housekeeping purposes.