1

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

我相信,这几乎与手头的问题无关。

这是给我带来麻烦的代码片段:

[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,创建一个包含项目高度和持续时间的新对象,并将 1 添加到$Counter

看起来直截了当,对吧?这里有一个问题:如果只有一个高度为 1080 in 的文件$FileList$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

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

4

1 回答 1

3

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

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


它与多个项目一起工作的原因$List是 PowerShell 的成员枚举然后返回一个属性值数组.VideoHeight其中索引[0]按预期工作。

它不能按预期使用单个项目的原因$List是只返回一个标量(单个).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[]] 数组

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

    • 严格来说,是什么语言消费了实现[...]索引器语法的类型,这是一种语法糖;底层类型只有一个名为的参数化属性Chars,C# 和 PowerShell 通过[...].

    • 但是,在 的情况下[string],不幸的是,这与 PowerShell 对集合和标量的统一处理相冲突,通常 $scalar[0]$scalar- 有关背景信息,请参阅此答案


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

于 2020-03-21T12:18:01.033 回答