From my understanding, the invoke operator (&) and the Invoke-Expression cmdlet should behave similar. However, as can be seen below, this is not the case:
PS C:\Users\admin> powershell -Command "& {""([Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('ZWNobyAnaGVsb
G93b3JsZCc=')))""}"
echo 'helloworld'
PS C:\Users\admin> powershell -Command "IEX ""([Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('ZWNobyAnaGVs
bG93b3JsZCc=')))"""
helloworld
Here, 'ZWNobyAnaGVsbG93b3JsZCc='
is the Base64 encoded string "echo helloworld"
.
Can someone clarify?
a.invoke(i_1, ..., i_n) Parentheses are translated to calls to invoke with appropriate number of arguments.
+= Add a to b and assign to a. -= Subtract b from a and assign to a. *= Multiply a and b and assign to a. /= Divide a by b and assign to a. %= Divide a by b and assign the remainder to a.
Since Kotlin provides user-defined types, it also provides the additional functionality to overload the standard operators, so that working with user-defined types is easier. All of the unary, binary, relational operators can be overloaded.
Invoke-Expression
(whose built-in alias is iex
) and &
, the call operator, serve different purposes:
Invoke-Expression
evaluates a given string as PowerShell source code, as if you had executed the string's content directly as a command.
As such, it is similar to eval
in bash
and therefore only to be used with input that is fully under the caller's control or input that the caller trusts.
There are often better solutions available, so Invoke-Expression should generally be avoided
&
is used to invoke a command (& <nameOrPath> [...]
) or a script block (& { ... } [...]
):
In the case at hand:
The core of your command is the following expression, which returns the string"echo 'helloworld'"
(its content doesn't include the enclosing "
- this is simply the representation of the resulting string as a PowerShell string literal):
[Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('ZWNobyAnaGVsbG93b3JsZCc='))
Also note that, due to how the command line is parsed, the ""...""
surrounding the core expression in your original commands are effectively ignored, which explains why the expression is executed rather than being treated as the content of a string.[1]
Therefore, your two commands amount to:
& { "echo 'helloworld'" }
&
executes the statement inside the script block, which happens to be a string, and a string by itself - if it isn't assigned to a variable or redirected elsewhere - is simply output as-is.
In this case, the command is effectively the same as just executing "echo 'helloworld'"
by itself (including the enclosing "
, which you can think of asecho "echo 'helloworld'"
), so echo 'helloworld'
prints to the console.
Note that echo
is a built-in alias for the Write-Output
cmdlet, whose explicit use is rarely necessary: Return values from commands or expressions are implicitly output, if they are not captured in some form, as in this case, where executing a string by itself as a statement simply outputs the string. (You can try this by submitting just 'hi'
at the prompt, for instance).
iex "echo 'helloworld'"
iex
(Invoke-Expression
) evaluate the string's content as source code, which therefore executes echo 'helloworld'
, which prints helloworld
to the console.Note:
Handling of quoting with respect to external programs or when calling from an external programs is not part of the official documentation, as far as I can tell (as of this writing, neither about_Parsing nor about_Quoting_Rules nor about_Special_Characters mentions it - I've opened this issue on GitHub to address that).
There are flaws in the existing handling, but they cannot be fixed without breaking backward compatibility.
When calling from PowerShell, the best approach is to use a script block, which bypasses the quoting problems - see below.
Even though you correctly embedded "
by escaping them as ""
inside the overall "..."
string from a PowerShell-internal perspective, additional escaping of "
with \
is needed in order to pass them through to an external program, even if that external program is another instance of PowerShell called via powershell.exe
.
Take this simplified example:
powershell.exe -command " ""hi"" " # !! BROKEN
powershell.exe -command ' "hi" ' # !! BROKEN
PowerShell-internally, " ""hi"" "
and ' "hi" '
evaluate to a string with literal contents "hi"
, which, when executed, prints hi
.
Regrettably, PowerShell passes this string to powershell.exe
as " "hi" "
- note how the ""
turned into plain "
and the enclosing single quotes were replaced with double quotes - which effectively results in hi
after parsing by the new instance (because " "hi" "
is parsed as the concatenation of substrings " "
, hi
, and " "
), so PowerShell ends up trying to execute a (presumably nonexistent) command named hi
.
By contrast, if you manage to pass the embedded as "
as \"
(sic) - after meeting PowerShell's own escaping needs - the command works as intended.
Therefore, as stated, you need to combine PowerShell-internal escaping with for-the-CLI escaping in order to pass an embedded "
, so that:
"..."
, each embedded "
must be escaped as \""
(sic) or \`"
(sic)'...'
, \"
can be used as-is.powershell.exe -command " \""hi\"" " # OK
powershell.exe -command " \`"hi\`" " # OK
powershell.exe -command ' \"hi\" ' # OK
Alternatively, use a script block instead of a command string, which bypasses the quoting headaches:
powershell.exe -command { "hi" } # OK, but only works when calling from PS
Note that the script-block technique only works when calling from PowerShell, not from cmd.exe
.
cmd.exe
has its own quoting requirements:
Notably, cmd.exe
only supports ""
for embedding double quotes (not also `"
); thus, among the solutions above, onlypowershell.exe -command " \""hi\"" "
works from cmd.exe
(a batch file) without additional escaping.
The down-side of \""
, however, is that runs of interior whitespace between \""...\""
are collapsed to a single space each. To avoid that, use \"...\"
, but cmd.exe
then sees substrings between the \"
instances as unquoted, which would cause the command to break if that substring contained metacharacters such as |
or &
; e.g., powershell.exe -command " \"a|b\" "
; to fix that you must individually ^
-escape the following characters: & | < > ^
powershell.exe -command ' "hi" '
is similarly brittle, because cmd.exe
doesn't recognize '
as a string delimiter, so any metacharacters outside embedded "..."
are again interpreted by cmd.exe
itself; e.g., powershell.exe -command ' "hi" | Measure-Object '
Finally, using just ""
from cmd.exe
for embedding "
sometimes works, but not reliably; e.g., powershell.exe -command " 'Nat ""King"" Cole' "
prints Nat "King Cole
(the closing "
is missing).
This appears to have been fixed in PowerShell Core.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With