【问题标题】:Why is PSCustomObject being populated with individual characters instead of a single string?为什么 PSCustomObject 填充的是单个字符而不是单个字符串?
【发布时间】:2020-03-21 11:50:40
【问题描述】:

我正在尝试通过翻译我为使用 FFmpeg 转换视频而制作的旧批处理脚本来学习 PowerShell。

我相信这与手头的问题几乎没有关系。

这是给我带来麻烦的代码sn-p:

[string]$FileList              = (Get-Clipboard).Split("`n")
[int]$Counter               = 0

$List                  = @(ForEach ($i in $FileList)
{
    [PSCustomObject]
   @{
        VideoHeight = (ffprobe.exe -v error -select_streams v:0 -show_entries stream=height -of csv=s=x:p=0 "$i")
        VideoDuration = (ffprobe.exe -v error -select_streams v:0 -show_entries stream=duration -of csv=s=x:p=0 "$i")
    }
    $Counter++
})

将剪贴板中的文件列表存储在$FileList 中,并用新行分隔。

$Counter 中存储整数0

对于$FileList 中的每个项目,创建一个包含项目高度和持续时间的新对象,并将$Counter 加1。

看起来很简单,对吧?这里有个问题:如果$FileList 中只有一个高度为 1080 的文件,$List.VideoHeight[0] 将只返回 1,但如果 $FileList 中至少有两个文件,$List.VideoHeight[0] 将返回 1080 .

命令行输出:

Single File
$List.VideoHeight:
1080
$List.VideoHeight[0]:
1
Multiple Files
$List.VideoHeight:
1080
1080
720
$List.VideoHeight[0]:
1080

有什么想法吗?我被卡住了。

【问题讨论】:

  • 顺便说一句:我假设[string]$FileList 应该是[string[]]$FileList - 否则,您的foreach 循环将无法工作。

标签: powershell powershell-5.0 pscustomobject


【解决方案1】:

使用$List[0].VideoHeight,而不是$List.VideoHeight[0]

毕竟,从概念的角度来看,您想要获取第一个列表项 ($List[0]) 的 .VideoHeight 值,而不是整个列表的视频高度值 ($List.VideoHeight) 的第一个元素。[1]


它与$List 中的多个 项一起工作的原因是PowerShell 的member-access enumeration 然后返回一个由.VideoHeight 属性值组成的数组,其中使用@987654333 进行索引@ 按预期工作。

$List 中的 single 项无法按预期工作的原因是,只有一个 标量(单个).VideoHeight 属性值被返回,并且该标量的类型为[string]。索引到单个字符串会返回字符串中的单个字符,这就是您所看到的。

简单演示:

PS> ([pscustomobject] @{ VideoHeight = '1080' }).VideoHeight[0]
1  # [char] '1', the first character in string '1080'

对比

PS> ([pscustomobject] @{ VideoHeight = '1080' }, 
     [pscustomobject] @{ VideoHeight = '1081' }).VideoHeight[0]
1080  # [string] '1080', the first element in array '1080', '1081'

因此,有两个因素会导致意外行为:

  • PowerShell 的成员访问枚举应用与收集管道输出时相同的逻辑:如果正在枚举其成员的集合恰好只有 一个 元素,则返回该成员(属性)值 原样;只有 2 个或更多元素会导致 [object[]] array 值。

    • 不幸的是,成员访问枚举的行为方式是这样的;当属性值本身是数组时,这种行为会更加令人惊讶:结果数组随后会连接,而不是成为输出数组中的单独子数组 - 请参阅this GitHub issue
  • .NET System.String 类型 ([string]) 的标准行为,它允许对构成字符串的字符进行直接索引(例如,"foo"[0] 产生 [char] 'f')。

    • 严格来说,是任何一种语言都使用了实现[...]索引器语法的类型,这是一种语法糖;底层类型只有一个名为Chars参数化属性,C# 和PowerShell 通过[...] 更方便地公开它。

    • 然而,在 [string] 的情况下,不幸的是,这与 PowerShell 对集合和标量的统一处理相冲突,其中 通常 $scalar[0]$scalar 相同 - 请参阅 this answer背景资料。


[1] If 收集所有$List .VideoHeight 属性值的值一致 返回一个数组(为什么它没有在中解释第二部分),这两个语句在功能上是等价的,尽管$List[0].VideoHeight 仍然是可取的效率(不必构造属性值的中间数组)以及为了避免潜在的成员名称冲突,成员访问枚举固有地需要 - 请参阅this GitHub issue

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-06-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多