【问题标题】:Powershell ForEach-Object column variablesPowershell ForEach-Object 列变量
【发布时间】:2021-05-04 12:55:24
【问题描述】:

有点奇怪的问题。我有一个需要处理的大型 JSON 文件。基于另一个问题,我需要流式传输文件,否则它会因为内存而给我带来问题:JSON Powershell memory issue

我拥有的是这样的:

get-content -Path largefile.json | ForEach-Object {
$row = $_ = $_.TrimStart('[').TrimEnd(']')
if ($_) { $_ | Out-String | ConvertFrom-Json }
New-Item -Path $($Row.Id).txt
Set-Content -Path $($Row.Id).txt -Value ($row.Body)
}

我可以轻松地使用 $row 来发布 Largefile.json 中最后处理的行。我想在当前处理的行中创建一个名称为 Id 的文件,并将正文列添加到文件中。但是,当我想使用 $row.Id 显示特定列时,不幸的是,它显示为空。

Largefile.json的结构如下:

[{"Id":"1","ParentId":"parent","Name":"filename","OwnerId":"owner","CreatedDate":"date","Body":"data1"}
{"Id":"2","ParentId":"parent","Name":"filename","OwnerId":"owner","CreatedDate":"date","Body":"data2"}
{"Id":"3","ParentId":"parent","Name":"filename","OwnerId":"owner","CreatedDate":"date","Body":"data3"}
{"Id":"4","ParentId":"parent","Name":"filename","OwnerId":"owner","CreatedDate":"date","Body":"data4"}
{"Id":"5","ParentId":"parent","Name":"filename","OwnerId":"owner","CreatedDate":"date","Body":"data5"}
]

最终结果应该是我有5个文件:

  • 1.txt - 文件内的值应该是:data1

  • 2.txt - 文件内的值应该是:data2

  • 3.txt - 文件内的值应该是:data3

  • 4.txt - 文件内的值应该是:data4

  • 5.txt - 文件内的值应该是:data5

我使用的是 Powershell 7.1.3

有什么方法可以让我像普通的 ForEach 一样使用 $row.Id 和 $row.ParentId 吗?

感谢您的帮助。

【问题讨论】:

  • 好的,我花了一段时间才明白你在哪里寻找,但我认为这就是答案:与 ForEach statement 相比,ForEach-Object (alias ForEach) cmdlet 有一个 自动变量$_ or $PSItem,代表当前项($Row
  • 我知道,但是当我运行 $_.Id 时,它应该只显示 Id 但它只是显示为空
  • 如果我仍然没有抓住重点,请使用下面我的答案中的示例数据(带有唯一 ID 等),定义您将“经常”使用的语句以及您所期望的完全结果基于示例数据。换句话说,在您的问题中创建一个看起来像实际情况的minimal reproducible example
  • 我得到了无效的 json 数组。它缺少逗号。该代码也有“无效的 json 原始错误”。
  • 我们还没有收到您的来信.. 任何给定的答案是否解决了您的问题?如果是这样,请通过单击左侧的 图标来考虑accepting。这将帮助其他有类似问题的人更轻松地找到它,并有助于激励其他人回答您将来可能遇到的任何问题。

标签: json powershell foreach-object


【解决方案1】:

在我看来,这就是你要找的东西:

Get-Content largefile.json | ForEach-Object {
    $row = $_.TrimStart('[').TrimEnd(']') | ConvertFrom-Json
    if ($null -ne $row) {
        Set-Content -Path ($row.Id) -Value ($row.Body)
    }
}

【讨论】:

  • 这仅适用于第一行,之后我得到: ConvertFrom-Json: Line | 2 | $row = $_.TrimStart('[').TrimEnd(']') | ConvertFrom-Json | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~从 JSON 转换失败并出现错误:完成读取 JSON 内容后遇到的附加文本:,。路径'',第 1 行,位置 0。它现在将一遍又一遍地处理同一行
  • @vulkoek 错误消息表明该行有一个尾随逗号。您的示例数据不是这样格式化的。你可能只需要使用$_.TrimStart('[').TrimEnd(']').TrimEnd(','),但是因为我没有你的JSON输入的有效示例,所以这完全是在黑暗中拍摄。
【解决方案2】:

我仍然不确定您期望的结果。
但我认为你想这样做:

@'
[{"Id":"1","ParentId":"parent1","Name":"1.txt","OwnerId":"owner","CreatedDate":"date","Body":"Data1"}
{"Id":"2","ParentId":"parent2","Name":"2.txt","OwnerId":"owner","CreatedDate":"date","Body":"Data2"}
{"Id":"3","ParentId":"parent3","Name":"3.txt","OwnerId":"owner","CreatedDate":"date","Body":"Data3"}
{"Id":"4","ParentId":"parent4","Name":"4.txt","OwnerId":"owner","CreatedDate":"date","Body":"Data4"}
{"Id":"5","ParentId":"parent5","Name":"5.txt","OwnerId":"owner","CreatedDate":"date","Body":"Data5"}
]
'@ | Set-Content .\largefile.json

Get-Content .\largefile.json | ForEach-Object {
    $_ = $_.TrimStart('[').TrimEnd(']')
    If ($_) { 
        $Row = ConvertFrom-Json $_
        Set-Content -Path ".\$($Row.Name)" -Value $Row.Body
    }
}

【讨论】:

  • 我在问题中添加了更多信息,它表明我想检索一个值来创建一个文件。让我知道这是否可以澄清。并为新手问题道歉。
【解决方案3】:

这个问题有很多错误。假设 json 中缺少逗号,如果我理解这个问题,我会这样做。这应该适用于问题的新更新。我这里还有一个更不寻常的解决方案,涉及使用 jq 流式传输 json:Iterate though huge JSON in powershell 以后可能会添加 Json 流式传输支持:ConvertFrom-JSON high memory consumption #7698

[{"Id":"ID","ParentId":"parent","Name":"filename","OwnerId":"owner","CreatedDate":"date","Body":"*******"},
 {"Id":"ID","ParentId":"parent","Name":"filename","OwnerId":"owner","CreatedDate":"date","Body":"*******"},
 {"Id":"ID","ParentId":"parent","Name":"filename","OwnerId":"owner","CreatedDate":"date","Body":"*******"},
 {"Id":"ID","ParentId":"parent","Name":"filename","OwnerId":"owner","CreatedDate":"date","Body":"*******"},
{"Id":"ID","ParentId":"parent","Name":"filename","OwnerId":"owner","CreatedDate":"date","Body":"*******"}
]
get-content -Path largefile.json | ForEach-Object {
  $_ = $_.TrimStart('[').TrimEnd(']').TrimEnd(',')
  if ($_) {
    $row = $_ | ConvertFrom-Json
    Set-Content -Path ($Row.Id + '.txt') -Value $row.Body
  }
}
get-content ID.txt

*******

【讨论】:

    【解决方案4】:

    正如其他人已经解释的那样,您的 json 示例无效。

    但是,由于这是一个要处理的巨大文件,因此您可以使用switch

    switch -Regex -File D:\Test\largefile.json {
        '"Id":"(\d+)".*"Body":"(\w+)"' { 
            Set-Content -Path ('D:\Test\{0}.txt' -f $matches[1]) -Value $matches[2]
        }
    }
    

    使用您的示例的结果将是 5 个名为 1.txt .. 5.txt 的文件,每个文件都有一行 data1 .. data5

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-12-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多