I can't run the code when I use powershell.exe -command - powershell

Cold you help me with following problem?
How to run the
"{0:n10}" -f 21.21
code in
powershell.exe -command "& {}"
argument?

There's no reason to use "& { ... }" in order to invoke code passed to PowerShell's CLI via the -Command (-c) parameter - just use "..." directly.
Older versions of the CLI documentation erroneously suggested that & { ... } is required, but this has since been corrected.
Unescaped " characters on the command line are stripped before the resulting argument(s) following -Command (-c) are interpreted as PowerShell code. " characters that need to be preserved as part of the PowerShell command to execute therefore need escaping.
From PowerShell's perspective, using \" works, in both editions.
Additionally, to prevent inadvertent whitespace normalization (folding of multiple spaces into one), the entire command to pass to -Command (-c) should be passed inside a single, "..." string overall:
Therefore (note that this assumes calling from outside PowerShell):
powershell.exe -Command " \"{0:n10}\" -f 21.21"
However, when calling from cmd.exe, specifically, \" escaping may break cmd.exe's syntax, in which case a different form of escaping of embedded " chars. is required - the following works robustly:
"^"" (sic) with powershell.exe, the Windows PowerShell CLI:
powershell.exe -Command " "^""{0:n10}"^"" -f 21.21"
"" with pwsh.exe, the PowerShell (Core) 7+ CLI:
pwsh.exe -Command " ""{0:n10}"" -f 21.21"
See this answer for details.

Related

On WSL2 how do I get a powershell command's output to print to stdout

In WSL2 I want to run a powershell command and get the output of the command to print to stdout on my WSL2.
I have tried many variants but it seems I always just get the command given back and not its output:
$ pwsh.exe -Command {Get-Date}
Get-Date
$ pwsh.exe -Command '{Write-Output "hello"}'
Write-Output "hello"
You should not use curly braces here :
pwsh.exe -Command 'Get-Date'
Philippe's helpful answer provides the crucial pointer:
When calling pwsh, the PowerShell (Core) CLI, from outside PowerShell:
Do not use { ... } to enclose your command(s) - that only works from inside PowerShell.
Inside PowerShell, { ... } creates a script block literal, which is a reusable piece of PowerShell code that can be invoked on demand.
If you pass { ... } to pwsh from inside PowerShell, special processing happens behind the scenes, which notably preserves type fidelity in the output as much as possible - see this answer for details.
It should be noted that calling the PowerShell CLI from PowerShell is rarely necessary, except, for instance, on Windows, if you want to call the respective other PowerShell edition's CLI.
Instead, pass your command(s) as-is, inside a string.
If you pass a string to -Command from outside PowerShell with { ... } embedded, what happens is that PowerShell creates a script block and - in the absence of invoking it, with & or . - its string representation is output, which is the block's verbatim content (excluding { and }) - that is what you saw.
As an aside: There are many examples out there that use "& { ... }" as a -Command argument; while that technically works, it is unnecessary, and passing just "..." (or '...', from POSIX-compatible shells) is sufficient.
Thus:
# Simple case: no quoting necessary
$ pwsh.exe -Command Get-Date
# Command string that requires quoting:
$ pwsh.exe -Command 'Write-Output "hello"'

How to solve problem with quotes in arguments on calling a PowerShell script with -File option?

I want to call a small PowerShell (v5.1) script from Windows 10 Pro command line with arguments.
param($ReqURL, $JSONString)
Write-Host $ReqURL # for debug
Write-Host $JSONString # for debug
Invoke-RestMethod -Uri $ReqURL -Method Patch -ContentType "application/json" -Body $JSONString
Calling the script in a PowerShell console works fine:
.\http-patch.ps1 -ReqURL myRESTApi -JSONString '{"lastname": "Bunny"}'
Calling the script from the Windows Command Prompt (cmd.exe) works when I escape the double quotes, like this:
PowerShell .\http-patch.ps1 -ReqURL myRESTApi -JSONString '{\"lastname\": \"Bunny\"}'
But when I use the -File option, it fails because the $JSONString inside the script is equal to '{"lastname"::
PowerShell -File "C:\work\LV Demo\http-patch.ps1" -ReqURL myRESTApi -JSONString '{\"lastname\": \"Bunny\"}'
I assume here are some problems with quotes, but I can't find the right way.
When you use the -File parameter of powershell.exe, the Windows PowerShell CLI, only double quotes (") are recognized as having syntactic function when calling from outside PowerShell, such as from cmd.exe:
Therefore, switch from ' to " (the embedded " chars. require escaping as \" (sic) either way):
:: From cmd.exe
PowerShell -File "C:\work\LV Demo\http-patch.ps1" -JSONString "{\"lastname\": \"Bunny\"}" -ReqURL myRESTApi
By contrast, when you use -Command, '...' strings are recognized, after unescaped " chars. - the only ones with syntactic function during initial command-line parsing - have been stripped, because the resulting tokens are then interpreted as PowerShell code.
For guidance on when to use -Command vs. -File and the differences in resulting parsing, see this answer.
From inside PowerShell, there's rarely a need to to call another PowerShell instance, given that .ps1 files can be invoked directly, in-process.
In cases where you do need to call the CLI from inside PowerShell - say you need to call the CLI of the other PowerShell edition, PowerShell (Core)'s pwsh.exe, from powershell.exe or vice versa - the best choice is to use a script block ({ ... }) (which works only when calling from inside PowerShell), because that:
allows you to focus solely on PowerShell's own quoting and escaping requirements.
supports receiving typed output (objects other than strings), behind-the-scenes XML-based serialization, albeit with limited type fidelity - see this answer.
# From PowerShell
# Note how the " chars. now need NO escaping.
# (If you were to use a "..." string, you'd escape them as `" or "")
PowerShell {
& C:\work\LV Demo\http-patch.ps1" -JSONString '{"lastname": "Bunny"}' -ReqURL myRESTApi
}
While you can call via individual arguments, analogous to how you must call from outside PowerShell, you'll not only lose the benefit of typed output, but you'll also run in a longstanding bug up to PowerShell 7.2.x that requires manual escaping of embedded " chars. as \ when calling external programs - see this answer - as evidenced by one of your own attempts (here, using '...' is perfectly fine, because PowerShell recognizes it):
# !! Note the unexpected need to \-escape the embedded " chars.
PowerShell -File .\http-patch.ps1 -JSONString '{\"lastname\": \"Bunny\"}' -ReqURL myRESTApi
# Variant with double quotes.
# !! Note the *double* escaping: first with ` - for the sake of PowerShell itself -
# !! then with \ due to the bug.
PowerShell -File .\http-patch.ps1 -JSONString "{\`"lastname\`": \`"Bunny\`"}" -ReqURL myRESTApi

Unexpected token '}' when passing string to PowerShell script

I have a script that accepts two strings, each are delimited by '|-|'. The second string can contain any character other than '.
To call the script I do:
PowerShell .\script.ps1 -arg1 'Hello|-|World' -arg2 'random|-|dwua0928]43d}'
It raises an error on the character }. How can I resolve this?
Edit:
It also has an issue with the - from the delimiters and raises the same error:
At line:1 char:54
+ .\Script.ps1 -arg2 <redacted>|-|<redacted> ...
+ ~
Missing expression after unary operator '-'.
At line:1 char:53
+ .\Script.ps1 -arg1 <redacted>|-|<redacted> ...
+ ~
Expressions are only allowed as the first element of a pipeline.
It does that at every instance of |-|. It does it with the brackets if I change the delimiter to /-/:
At line:1 char:168
+ ... <redacted>/-/<redacted>!OW}:Lj<redacted> ...
+ ~
Unexpected token '}' in expression or statement.
At line:1 char:181
+ ... /-/<redacted>cO0}e]k<redacted> ...
+ ~
Unexpected token '}' in expression or statement.
The beginning of the script is this:
Param
(
[switch] $enable,
[string] $arg1,
[string] $arg2
)
There's generally no need to call the PowerShell CLI (powershell.exe for Windows PowerShell, pwsh for PowerShell (Core) 7+) from inside PowerShell.
Assuming you're running Windows PowerShell (or, to generalize, the same edition of PowerShell), you can invoke your script directly:
.\script.ps1 -arg1 'Hello|-|World' -arg2 'random|-|dwua0928]43d}'
If you still want to call the CLI - which is expensive, because it involves creating a child process, and also results in a loss of type fidelity - use a script block ({ ... }), but note that this works only from inside PowerShell (though it works across both PowerShell editions, i.e., you can call pwsh.exe from Windows PowerShell, and powershell.exe from PowerShell (Core) 7+)
powershell { .\script.ps1 -arg1 'Hello|-|World' -arg2 'random|-|dwua0928]43d}' }
If you were to call from outside PowerShell, it is best to use the -File CLI parameter rather than the (implied in Windows PowerShell) -Command parameter:
powershell -File .\script.ps1 -arg1 "Hello|-|World" -arg2 "random|-|dwua0928]43d}"
While this works from inside PowerShell too, the disadvantage compared to the { ... } approach is that the output is only ever text, whereas the { ... } approach is - within limits - capable of preserving the original data types - see this answer for more information.
In order to make a -Command-based command line work, enclose the PowerShell code in "..." as a whole, in which case the embedded '...' strings are passed as such:
powershell -Command ".\script.ps1 -arg1 'Hello|-|World\' -arg2 'random|-|dwua0928]43d}'"
For a comprehensive overview of the PowerShell CLI, see this post.
As for what you tried:
You called powershell.exe with neither -File (-f) nor -Command (-c), which defaults to -Command (note that, by contrast, pwsh, the PowerShell (Core) 7+ CLI, defaults to -File).
When -Command is used, PowerShell's command-line processing first strips all subsequent arguments of any syntactic (i.e., unescaped) " characters, namely from "..."-enclosed arguments, joins the resulting tokens with spaces, and then interprets the resulting string as PowerShell code.
In your case, the fact that you used '...' quoting around the arguments is irrelevant:
PowerShell invariably translates the original quoting into "..." quoting when calling external programs (as they can only be expected to "..." quoting, not also '...' quoting)
However, it only uses "..." quoting if needed, which is solely based on whether the argument at hand contains spaces.
Neither of your arguments, verbatim Hello|-|World and random|-|dwua0928]43d}, contained spaces, so they were placed as-is on the command line for powershell.exe (though note that even if they did contain spaces and had been enclosed in "..." as a result, PowerShell's command-line processing would have stripped the " chars.)
The net result was that the powershell.exe child process tried to execute the following PowerShell code, which predictably breaks with the errors shown in your question:
# !! Note the absence of quoting around the -arg1 and -arg2 values.
.\script.ps1 -arg1 Hello|-|World -arg2 random|-|dwua0928]43d}
You can easily provoke the error you saw as follows from a PowerShell session:
# -> Error "Missing expression after unary operator '-'."
Write-Output Hello|-|World

Cannot access $Profile.CurrentUserAllHosts using pwsh.exe?

I want to read the automatic variable $profile.CurrentUserAllHosts, using a Deno script.
I already know how to execute pwsh.exe from Deno.
But would like to know how to invoke pwsh.exe to get the $profile.CurrentUserAllHosts value.
For example, I expect that two commands below yields the same results rather than different results:
PS> $profile.CurrentUserAllHosts
C:\Users\Flavio\Documents\PowerShell\profile.ps1
PS> pwsh.exe -noprofile -Command "{$profile.CurrentUserAllHosts}"
C:\Users\Flavio\Documents\PowerShell\Microsoft.PowerShell_profile.ps1.CurrentUserAllHosts
Note: I'm using Powershell 7.2.5, on Windows 10 and I can guarantee that pwsh.exe is the same version for all instances.
There's two things to keep in mind here:
You're running the command in PowerShell itself, so $profile is actually expanded in your parent-session due to the double-quotes in your -Command argument.
You're passing a string with a scriptblock as the -Command argument. It's not actually executed - you're just getting the result of {$profile.CurrentUserAllHosts}.ToString() which is the content of the scriptblock.
Suggestions:
Add a call operator to invoke the scriptblock in the string, ex -Command '& { $profile.CurrentUserAllHosts }'. If invoking pwsh from PowerShell you can also use pwsh.exe -noprofile -Command { $profile.CurrentUserAllHosts }.
Or provide the command as a simple string, ex. -Command '$profile.CurrentUserAllHosts'
In both alternatives, remember single quotes OR escaping the dollar-sign with backtick.

How to download a file to $env:temp directory using powershell from cmd console?

I can download a file to a constant location from cmd console using powershell like this
C:\Users\Weijun>powershell -command "(new-object System.Net.WebClient).DownloadFile('https://example.com/example.zip', 'D:\example.zip')"
but i need to download it to system temp directory,using the powershell variable $env:temp,the following doesn't work.(variable not expanded in single-quote)
powershell -command "(new-object System.Net.WebClient).DownloadFile('https://example.com/example.zip', '$env:temp\example.zip')"
how to pass variable as parameter in method invoke?
UPDATE: it is a typo for $env:temp:,I amend it.
tl;dr:
From outside of PowerShell (cmd.exe - Command Prompt or batch file), use escaped double-quoted strings - \"...\" - inside the overall command string - "..." - passed to powershell -command for strings that need interpolation (e.g., expansion of variable references such as $env:temp):
powershell -command "(new-object System.Net.WebClient).DownloadFile('https://example.com/example.zip', \"$env:temp\example.zip\")"
You not only need to fix the incidental problem that Nkosi points out - removing the extraneous : at the end of $env:temp: - but you must generally either not quote the path at all (argument syntax), or use escaped double quotes (expression syntax or values with shell metacharacters) - single-quoted strings don't work, because PowerShell treats their content as a literal and won't expand the environment-variable reference.[1]
For a description of PowerShell's two fundamental parsing modes - argument syntax vs. expression syntax - see Get-Help about_Parsing.
Simplified examples:
Without quoting (argument syntax):
c:\>powershell.exe -noprofile -command "write-output $env:temp\example.zip"
C:\Users\jdoe\AppData\Local\Temp\example.zip
With double-quoting (expression syntax or if the value contains shell metacharacters such as , or |):
c:\>powershell.exe -noprofile -command "write-output \"$env:temp\example.zip\""
C:\Users\jdoe\AppData\Local\Temp\example.zip
Note: When called from the outside, PowerShell requires embedded " chars. to be escaped as \" - unlike inside PowerShell, where `" must be used.
Applied to your command: with (escaped) double-quoting, which is needed, because you're using .NET method syntax, which is parsed with expression syntax:
powershell -command "(new-object System.Net.WebClient).DownloadFile('https://example.com/example.zip', \"$env:temp\example.zip\")"
[1] If you run the same command from PowerShell, you won't see the problem, because the command as a whole is enclosed in "...", which means that the current PowerShell session will expand $env:temp - before it is passed to powershell.exe.
However, when called from cmd.exe, this interpolation is NOT applied - hence the need to either omit quoting or use embedded (escaped) double-quoting.
I would use Invoke-WebRequest with -OutFile:
powershell -command "Invoke-WebRequest -Uri 'http://example.com/file-to-get.txt' -OutFile $env:temp\file-to-get.txt"
NB this requires PowerShell 3.0 (if I remember correctly).