New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.
Already on GitHub? Sign in to your account
PowerShell should support passing verbatim args #12975
Comments
PowerShell is a shell - just like This means: you need to understand and accommodate the running shell's syntax rules, and you need to understand that if you submit something like: wsl cd / && ls . | cat -n that any unquoted token is subject to PowerShell's syntax rules; specifically, Additionally, you need to understand the external wsl cd / '&&' ls . '|' cat -n The alternative is to explicitly pass an entire command line as a single string to the default WSL shell, wsl sh -c 'cd / && ls . | cat -n' There is nothing that PowerShell can reasonably do to ease this perceived pain; that the ill-fated and Windows-focused Given that an understanding of the syntax requirements allows you to solve the problem, I don't think any new syntax is called for. |
Yes, after discussing with @SteveL-MSFT, this behaviour seems to be quite explicitly coded for: PowerShell/src/System.Management.Automation/engine/parser/tokenizer.cs Lines 2311 to 2315 in a3319d7
To be clear, the verbatim argument parameter is terminated by As @mklement0, the solution is to quote the One thing I've noticed though is that in the sparse documentation of It's not really clear to me why this choice was made, but even constrained in scope as it is, I think we'd need to have very strong motivation to break it.
I imagine you're making a general statement, but I suspect @bitcrazed is reasonably familiar with those 馃檪 |
I don't think it's covered in the official documentation, but I've summarized the behavior and limitations in this Stack Overflow answer; there's also this one, which focuses on Unix-like platforms.
Well, I wasn't, until I took a closer look: |
@mklement0 Yes, I am very familiar with shells and shell syntax. The problem is that the lack of an ability to delimit a portion of a command-line to be passed varbatim to the receiving command/script is something that trips users up all the time. The stop parsing symbol (which, by the way is practically impossible to search for) is only documented here (as far as I could find) and states (emphasis mine):
This is a perfect example of why I believe we need some kind of enclosure, rather than expecting users to escape their command-line actions correctly: Stop parsing up until some incorrectly/poorly documented arbitrary char/sequence seems to defeat the point of "stop parsing". PowerShell is awesome, but has some major impediments and barriers to adoption that need attention. Heck, this issue tripped-up me (owner of Cmd, PM for WSL & Terminal) and @SteveL-MSFT (owner of PowerShell) in an interactive WTH session yesterday afternoon. And this isn't just an issue that affects WSL: It also affects Windows Terminal's |
Then you are undoubtedly familiar with the need to escape or quote the calling shell's metacharacters if you want to pass them through (use them verbatim). Doing so gives us the aforementioned solution: PS> wsl cd / '&&' ls . '|' cat -n
# Alternative, with escape char.
PS> wsl cd / `&`& ls . `| cat -n Incidentally, you would in essence normally have to do the same if you called from :: BREAKS! because `wsl` apparently doesn't strip the double quotes and `bash` sees them
:: verbatim - yet you need the double quotes in order to pass && and | through.
C:\>wsl cd / "&&" ls . "|" cat -n However, using the escape character (
This invariably entailed problematic compromises, and, in my opinion, Focusing just on the
The underlying challenge is that PowerShell has additional metacharacters, and that one of them - the escape character - differs from that of legacy shells.
I've previously summarized the issue in #1995 (comment), where I suggest an approach other than The introduction of a As an aside: the linked issue is primarily about a truly broken aspect of PowerShell, which has to date not been fixed, because the fix would be a severe breaking change: how arguments with embedded double quotes are passed to external programs. If # OK - command string is passed through as a literal
PS> bash.exe -c 'cd / && ls . | cat -n' Note that you may optionally combine this approach with up-front interpolation by PowerShell, so as to embed PowerShell variables / expressions, via In a manner of speaking, the single-quoted [here]-strings then provide the enclosure you're looking for. This approach also allows you to call from :: OK
C:\>bash.exe -c "cd / && ls . | cat -n" In cases where you do need to pass arguments verbatim individually, PowerShell offers another method, albeit not a very convenient one: array-based splatting: # OK
PS> wsl ('cd', '/', '&&', 'ls', '.', '|', 'cat', '-n') In this simple case you can ease the pain a bit, but it's not exactly obvious (and won't work with arguments with embedded spaces): # OK
PS> wsl (-split 'cd / && ls . | cat -n') However, I would expect that the far more typical case will be where calling the platform-native shell via a single command string is feasible, in which case To summarize:
I definitely understand the pain of the issue, but I also think that As PowerShell grows in popularity, the differing syntax needs will become more widely known, and the pain of not knowing how to modify command lines written for other shells / not understanding why there is even a need to will lessen. To me, the best we can do is:
|
What do you mean by "portion of a command-line to be passed varbatim"? Do you mean
Or do you mean
If you mean (1.), then it essentially already works:
(Except for the problem of embedded quotes as discussed in #1995) One could argue, that adding a one-line-here-string would improve some usecases, but I think, that's not really the point of this issue. As this does already work more or less, I assume, you meant (2.) If you mean (2.), then let me state my opinion on that in a somewhat dramatic way: Please please please don't add special syntax for this. This is basically what Why am I so strongly against this?
So the only thing missing for this explicit usecase is IMO a parameter to |
To bash it actually makes perfect sense, to pass
It's not bash that can't cope with |
If #13068 was implemented (and I hope it will not - at least not as yet another spedial syntax/operator), I guess this issue would be resolved with that. |
|
@TSlivede's point was that you currently cannot pass the meant-for- Aside from that, (Recognizing |
My understanding is that |
By limiting design, because not only would passing the command line as a single string ease the escaping woes that prompted this issue, it would allow full access to # Note: The following does NOT work with `bash.exe` on Windows, but works *from inside WSL*
# (and from POSIX-like shells on Unix-like platforms)
$ bash -c 'echo $# args; printf "%s!" $@' - one two
2 args
one!two! That is, Neither Note that, by contrast, with
Hypothetically, if you had a command named, say, literally Update: It is |
|
Good point, I've also corrected the comment above. The reason is that However, # The proper way to make bash treat -u as an operand (the script), not as an option.
bash -c -- -u Alas, this again doesn't work via PS> wsl.exe -- -- -u
/bin/bash: --: invalid option So, yes, your workaround via the Similarly:
Kudos for figuring out a workaround. I'm pretty sure we're on the same page, but just to spell it out: These workarounds are (a) far from obvious and (b), more importantly, shouldn't be necessary. The problems discussed are the problems of The proper solution is to allowing passing of an ad-hoc script / command line as a single string, while also supporting additional arguments to pass to the former. In concrete terms, without breaking backward compatibility, this could mean:
|
|
Reading through this issue is bittersweet. At first I was glad to see I wasn鈥檛 alone in getting tripped up in the idiosyncratic way PowerShell approach argument; but I鈥檓 saddened that years later this is still fundamentally broken. It鈥檚 maddening the hoops one has to jump through just to pass arguments. |
You can pick either this. https://github.com/mklement0/Native
|
Referece: I belive I hit the same issue here. PowerShell should not act smart and pass the incorrect args. You can see that it splits
It does work if I use |
@mcuee, that is one of several bugs in the parameter binder, and as such a separate issue (the workaround here is to pass
|
This issue has not had any activity in 6 months, if this is a bug please try to reproduce on the latest version of PowerShell and reopen a new issue and reference this issue if this is still a blocker for you. |
1 similar comment
This issue has not had any activity in 6 months, if this is a bug please try to reproduce on the latest version of PowerShell and reopen a new issue and reference this issue if this is still a blocker for you. |
This issue has not had any activity in 6 months, if this is a bug please try to reproduce on the latest version of PowerShell and reopen a new issue and reference this issue if this is still a blocker for you. |
This issue has been marked as "No Activity" as there has been no activity for 6 months. It has been closed for housekeeping purposes. |
Following a conversation with @SteveL-MSFT where he confirmed I am not (entirely) insane 馃槣
PowerShell currently makes if VERY difficult to
For example, trying to pass a Linux command-line
cd / && ls . | cowsay
towsl
in PowerShell 7+:wsl cd / && ls .
Expected: Directory listing of the root of the users' default Linux distro
Actual: Error reporting that cowsay is not a recognized command:
As can be seen from the syntax color highlighting, PowerShell is parsing
&&
as a PowerShell command continuation, not as a part of the command to be passed towsl
.So, how about wrapping the args in quotes?
Doesn't work because that would make
arg[1]
'cd / && ls . | cowsay'
which wouldn't make sense to bash.@SteveL-MSFT recommended prefixing the command-line args with
--%
to indicate that PowerShell should pass the remaining command-line verbatim. Alas, no joy here - notice the syntax highlighting which indicates that the parser thinks the&&
is a PowerShell command which, in this case, it is not!Boo! 馃槦
Okay, so how about escaping the
&&
with a ````` (backtick)?Oh! PowerShell thinks that I meant to spawn the remaining command as a job. NOPE!
How about escaping each
&
?Closer!
Now, how about also escaping the pipe operator:
HUZZAH! 馃檶
Escaping many symbols in lengthy command-lines could get tedious pretty quickly, especially when pasting more lengthy & complex bash command-lines into WSL, or complex page splits into
wt
(Windows Terminal):While
--%
may be useful (when fixed) in some cases to pass the remainder of this command-line to the tool being executed, commands couldn't be chained.So, ideally, PowerShell should (also) provide a way of escaping a specific portion of a command-line to the preceding tool, but then continue treating the remaining command-line as PowerShell script.
Perhaps enclosing the command-line segment in some of the following delimiters would work?
<
...>
, e.g.wsl <cd / && ls . | cowsay>
[
...]
, e.g.wsl [cd / && ls . | cowsay]
{
...}
, e.g.wsl {cd / && ls . | cowsay}
<--
...--<<<
, e.g.wsl <-- cd / && ls . | cowsay --<<<
馃槣... but will leave the choice of the enclosing delimiters to the language designers 馃榿
The text was updated successfully, but these errors were encountered: