【问题标题】:How to pass JSON (string data) to PowerShell?如何将 JSON(字符串数据)传递给 PowerShell?
【发布时间】:2018-11-26 23:02:09
【问题描述】:

我将以下内容作为参数传递给 powershell(w7 上的 v4):

-debugWrite -fileName SettingsFile -jsonContent { "c": "some setting", "d":  "unknown", "b": "some thing", "a": 1 }

但是 PS 在 JSON 上挂断了。我试过分隔 \double-quotes\ 并将 -jsonContent 之后的所有内容放在'单引号'中,但无济于事。

这是运行 PS 的 Windows 7 (PS4) 环境:

注意:“...”混淆指的是同一个目录。 IOW,所有文件都在同一个目录中。

运行一个批处理文件,开始整个过程​​:

    "C:\...\script.bat" > "C:\...\script-output.txt" 2>&1

这运行script.bat 并输出到script-output.txtscript.bat 是一个很长的 1-liner:

%WINDIR%\sysnative\windowspowershell\v1.0\powershell.exe -ExecutionPolicy Bypass -File "C:\...\customscript.PS1" --% -fileName DropFileToCreate -jsonContent "{     "c":  "some setting",     "d":  "unknown",     "b":  "some thing",     "a":  1 }" -debugWrite

传说:

DropFileToCreate - 传递给 PS 脚本的文件名,用于在同一目录中创建文件。

-jsonContent - 脚本中的命名参数(customscript.PS1 的标头见下文)

在上面的例子中,JSON 是:

"{ "c": "一些设置", "d": "未知", "b": "一些 东西", "a": 1 }"

-debugWrite - 开关参数(此处用于启用 Write-Host 调试)

最后一点customscript.PS1

Param (
    [Parameter(Mandatory = $True)]
    [String]
    $fileName,
    [Parameter(Mandatory = $True)]
    $jsonContent,
    [Parameter(Mandatory = $False)]
    [Switch]
    $debugWrite = $False
)
[...]

如果表示为,则 JSON 更易于查看,并且可以解释空格:

{
 "c": "some setting",
 "d": "unknown",
 "b": "some thing",
 "a": 1 
}

【问题讨论】:

    标签: json powershell batch-file syntax quoting


    【解决方案1】:

    tl;dr

    您的整个"..."-enclosed JSON 字符串已嵌入 ",必须将其转义为\"(原文如此;命令简化):

    powershell.exe -File "C:\...\customscript.PS1" ... -jsonContent "{ \"c\": \"some setting\", \"d\": \"unknown\", \"b\": \"some thing\", \"a\": 1 }"
    

    请继续阅读,了解何时需要额外转义、-File 调用与 -Command 调用有何不同,以及调用 shell(您从哪里调用 powershell.exe)的重要性。


    注意:

    • 此答案主要讨论 Windows PowerShell 可执行文件 powershell.exe 的使用,但它类似地适用于 PowerShell Core 可执行文件 pwsh,并且有一个部分从底部的bash 拨打电话。

    • 下面的从 PowerShell 本身调用部分,特别是 -File 所需的语法,也适用于将 JSON 传递给其他程序,例如 curl.exe

    PowerShell CLI 所需的语法 - 即使用参数调用powershell.exe - 取决于

    • 无论您是从 cmd.exe(命令提示符/批处理文件)还是从 PowerShell 本身(或者,在 PowerShell Core 中从类似 POSIX 的 shell,例如 bash)调用。

    • 您是否将参数传递给powershell -Command(内联命令)或
      powerShell -File(脚本路径)。

    无论哪种方式,您最初的尝试都无法奏效,因为 包含空格,无法将文字 { "c": "some setting" ... } 识别为单个参数 strong> 并且 整体上没有用引号括起来;稍后添加的命令,包含 "...",缺少对嵌入的 " 的转义。

    以下命令使用简化的 JSON 字符串演示了所讨论场景所需的语法。

    要使-File 命令可运行,请在当前目录中创建script.ps1 文件。内容如下:ConvertFrom-Json $Args[0]


    cmd.exe/批处理文件调用

    • 嵌入的" 必须转义为\"(即使PowerShell-在内部你会使用`")。

    • 重要:

      • 如果 JSON 文本包含 cmd.exe元字符(总是在 \"...\" 运行之间),您必须^-单独转义它们,因为cmd.exe,由于没有将\" 识别为转义的",因此将这些子字符串视为未引用;例如,\"some & setting\" 必须转义为 \"some ^& setting\";这里需要转义的cmd.exe 元字符是:
        & | < > ^
    • cmd.exe-style 环境变量引用,例如 %USERNAME% 插值 - cmd.exe 没有文字 字符串语法,它只识别"...",插值会发生,就像在未引用标记中一样。
      如果您想按原样传递这样的标记,即 suppress 插值,转义语法取决于您是从 命令行 还是从 批处理文件,遗憾的是:使用前者的%^USERNAME%,使用后者的%%USERNAME%% - 有关血腥细节,请参见this answer

    • 注意-Command 调用如何通过将"..." 字符串包含在'...' 中来简单地添加另一层引用。这是必需的,因为-Command PowerShell 将其接收到的参数视为 PowerShell 源代码 而不是 文字参数(后者是 -File 的情况);如果不是封闭的'...',整个封闭的"..." 将在解释之前剥离

    -File

    # With a literal string:
    powershell -File ./script.ps1 "{ \"c\": \"some setting\", \"unknown\": \"b\" }"
    
    # With an expandable string (expanded by the caller):
    powershell -File ./script.ps1 "{ \"c\": \"some %USERNAME%\", \"unknown\": \"b\" }"
    

    -Command

    # With a literal string:
    powershell -Command ConvertFrom-Json '"{ \"c\": \"some setting\", \"unknown\": \"b\" }"'
    
    # With an expandable string (expanded by the caller):
    powershell -Command ConvertFrom-Json '"{ \"c\": \"some %USERNAME%\", \"unknown\": \"b\" }"'
    

    从 PowerShell 本身调用

    • 从 PowerShell 调用无需转义 cmd.exe 元字符,因为不涉及 cmd.exe

    • PowerShell 的字符串引用规则适用,这简化了事情,但遗憾的是,您仍然需要手动 \-escape 嵌入 " 字符。;背景见this GitHub issue

      • 更新:PowerShell Core 7.2.0-preview.5 引入了experimental featurePSNativeCommandArgumentPassing,从而消除了对本手册\-escaping 的需要;尽管在这种情况下很有希望,但不能保证实验性功能成为常规功能;从 PowerShell Core 7.2.0-preview.5 开始,该功能是朝着正确方向迈出的一步,但既有错误,也缺乏适用于 Windows 上 CLI 的重要调整 - 请参阅 GitHub issue #15143

      • 使用外部 '...' 引用可以简化嵌入引用的语法,但这会限制您传递 literal 字符串。

      • 使用外部"..." 允许您嵌入来自调用者的变量引用和表达式(由调用者扩展,在传递参数之前),但它使语法复杂化,因为嵌入的 " 必须双重转义为 \`"(原文如此):首先使用 ` 以符合 PowerShell-internal 语法,然后使用 \ 来满足 PowerShell CLI 的要求。

      • 如果您的 JSON 文本不是文字并存储在 变量 中,则必须传递
        $jsonVar -replace '"', '\"' 以执行必要的转义 - 请参阅 this answer

    使用-File 或调用外部程序,例如curl.exe

    # With a literal string:
    powershell -File ./script.ps1 '{ \"c\": \"some setting\", \"unknown\": \"b\" }'
    
    # With an expandable string (expanded by the caller):
    powershell -File ./script.ps1 "{ \`"c\`": \`"some $env:OS\`", \`"unknown\`": \`"b\`" }"
    

    -Command

    # With a literal string:
    powershell -Command ConvertFrom-Json '''"{ \"c\": \"some setting\", \"unknown\": \"b\" }"'''
    
    # With an expandable string (expanded by the caller):
    powershell -Command ConvertFrom-Json "'{ \`"c\`": \`"some $env:OS\`", \`"unknown\`": \`"b\`" }'"
    

    PowerShell Core:从bash调用

    • Bash 和 PowerShell 一样,既能理解扩展(插入)"..." 字符串,也能理解文字 '...' 字符串。

    • cmd.exe 不同,Bash 将\" 识别为转义的" 字符。在"..." 中,因此无需转义 Bash 的任何元字符。

    -File

    # With a literal string:
    pwsh -File ./script.ps1 '{ "c": "some setting", "unknown": "b" }'
    
    # With an expandable string (expanded by the caller):
    pwsh -File ./script.ps1 "{ \"c\": \"some $USER\", \"unknown\": \"b\" }"
    

    -Command

    # With a literal string:
    pwsh -Command ConvertFrom-Json \''{ "c": "some setting", "unknown": "b" }'\'
    
    # With an expandable string (expanded by the caller):
    pwsh -Command ConvertFrom-Json "'{ \"c\": \"some $USER\", \"unknown\": \"b\" }'"
    

    【讨论】:

      【解决方案2】:

      我建议使用变量来传递字符串。对于给出的示例 JSON,它完全避免了转义。 ConvertFrom-Json 可用作测试应用。

      首先在PowerShell中,观察

      PS C:\> $psJson = '{ "c": "some setting", "d": "unknown", "b": "some thing", "a": 1 }'
      PS C:\> ConvertFrom-Json $psJson
      
      c            d       b          a
      -            -       -          -
      some setting unknown some thing 1
      

      通过 CMD,我们可以达到相同的结果。从批处理中打开 PowerShell 时,它会继承环境。不要传递字符串,而是设置一个环境变量并像全局一样使用它

      C:\>set dosJson={ "c": "some setting", "d": "unknown", "b": "some thing", "a": 1 }
      
      C:\>powershell -command "& { ConvertFrom-Json $env:dosJson }"
      
      c            d       b          a
      -            -       -          -
      some setting unknown some thing 1
      

      编辑:

      需要指出的是,这适用于-Command 而不是-File,因为-File args 无法使用PowerShell 环境。当然,您可以在-Command 中运行脚本。使用一个简单的测试脚本test.ps1

      C:\>type test.ps1
      param ([string] $json)
      ConvertFrom-Json $json
      C:\>powershell -command "& { C:\test.ps1 $env:dosJson }"
      
      c            d       b          a
      -            -       -          -
      some setting unknown some thing 1
      

      所以问题中的script.bat 必须更改为-Command

      【讨论】:

        猜你喜欢
        • 2014-09-04
        • 2020-08-09
        • 2014-01-01
        • 1970-01-01
        • 2018-11-14
        • 2019-11-16
        • 2018-11-25
        • 1970-01-01
        • 2011-02-12
        相关资源
        最近更新 更多