Powershell:始终生成null文件(Compare-Object的输出)

时间:2017-06-17 16:49:48

标签: windows powershell file compare member-enumeration

this question最受欢迎的答案涉及以下Windows powershell代码(已修改以修复错误):

$file1 = Get-Content C:\temp\file1.txt  
$file2 = Get-Content C:\temp\file2.txt   
$Diff = Compare-Object $File1 $File2  
$LeftSide = ($Diff | Where-Object {$_.SideIndicator -eq '<='}).InputObject  
$LeftSide | Set-Content C:\temp\file3.txt

我总是得到一个零字节文件作为输出,即使我删除$ Diff行。

为什么输出文件总是为空,如何修复?

2 个答案:

答案 0 :(得分:3)

正如他常规所做的那样,{p> PetSerAl在对问题的评论中提供了关键指针(无意将其转化为答案):

成员枚举 - 能够访问集合上的成员(属性或方法)并将其隐式应用于< em>每个元素,其结果都是在数组中收集的,是introduced in PSv3

会员枚举不仅富有表现力且方便,而且 更快比其他方法

简化示例:

PS> ((Get-Item /), (Get-Item $HOME)).Mode
d--hs-   # The value of (Get-Item /).Mode
d-----   # The value of (Get-Item $HOME).Mode

.Mode应用于集合(...) - 附带的命令输出会导致在集合中的每个项目上访问.Mode属性,结果值返回作为数组(常规PowerShell数组,类型为[System.Object[]])。

警告:成员枚举像管道那样处理结果数组,这意味着:

  • 如果数组只有单个元素,则该元素的属性值直接返回 ,而不是单个元素数组:< / p>

    PS> @([pscustomobject] @{foo=1}).foo.GetType().Name
    Int32  # 1 was returned as a scalar, not as a single-element array.
    
  • 如果收集的属性值是本身数组,则会返回 flat 值数组:

    PS> @([pscustomobject] @{foo=1,2}, [pscustomobject] @{foo=3,4}).foo.Count
    4 # a single, flat array was returned: 1, 2, 3, 4
    

此外,成员枚举仅适用于获取(阅读)属性值,而不适用于设置(写入)它们。 This asymmetry is by design,以避免可能不必要的批量修改;在PSv4 +中,使用.ForEach('<property-name', <new-value>)作为最快的解决方法(见下文)。

这个方便的功能不可用,但是:

  • 如果您正在 PSv2上运行(明确地)
  • 如果集合本身具有指定名称的成员,则应用集合级成员。

例如,即使在PSv3 +中,以下内容也不执行成员枚举:

    PS> ('abc', 'cdefg').Length  # Try to report the string lengths
    2 # !! The *array's* .Length property value (item count) is reported, not the items'

在这种情况下 - 通常在 PSv2 中 - 需要采用不同的方法:

  • 最快替代方案,使用 foreach 语句 ,假设整个集合整体适合内存(这是使用成员枚举时隐含
PS> foreach ($s in 'abc', 'cdefg') { $s.Length }
3
5
  • PSv4 + 替代方案,使用收集方法 .ForEach(),同时也对整个系列进行操作:
PS> ('abc', 'cdefg').ForEach('Length')
3
5

注意:如果适用于输入集合,您还可以使用.ForEach('<prop-name>', <new-value>) 设置属性值,这是无法使用.<prop-name> = <new-value>的最快解决方法},即无法使用成员枚举设置属性值。

    使用管道
  • 最慢​​,但内存效率方法:

注意:如果您逐个处理项目,而不是在内存中收集结果,那么使用管道只会提高内存效率。

使用ForEach-Object cmdlet ,与Burt Harris' helpful answer中一样:

PS> 'abc', 'cdefg' | ForEach-Object { $_.Length }
3
5

仅对于属性(与方法相对),Select-Object -ExpandProperty是一个选项;它在概念上清晰简单,在性能方面几乎与ForEach-Object方法相当(对于性能比较,请参阅我的this answer的最后一部分):

PS> 'abc', 'cdefg' | Select-Object -ExpandProperty Length
3
5

答案 1 :(得分:1)

也许不是

     for pair in zip(a_list, b_list):
         func_a_b(*pair)

PowerShell 2可能更适用于:

Sub OpenCSvs()
    Dim sWs As String, Fn As String
    Dim Wb As Workbook
    Dim start As Double
    Dim total_time As String
    Dim my_path As String, my_ext As String, my_file As String

      start = Timer

      my_path = "C:\Users\michal\SkyDrive\csv\bossa\mstcgl_mst\"    'Source folder.
      my_ext = "*.mst"          ' all files with .mst extension.
      my_file = Dir(my_path & my_ext)     ' take the first file from my_path.

      Do While my_file <> ""
        Fn = my_path & my_file
        Set Wb = Workbooks.Open(Fn, Format:=2)
        sWs = ActiveSheet.Name
        With ActiveSheet
            .Rows(1).Insert
            .Range("a1").Resize(1, 7) = Array("Ticker", "day", "open", "high", "low", "close", "vol")
        End With
        ExportToAccess Fn, sWs
        Wb.Close (0)
        my_file = Dir()
      Loop

      total_time = Format((Timer - start) / 86400, "hh:mm:ss")
    MsgBox "This code ran successfully in " & total_time & " minutes", vbInformation
End Sub
Sub ExportToAccess(myFn As String, sWs As String)
    Dim PathOfAccess As String
    Dim strConn As String, strSQL As String

    PathOfAccess = "C:\Database6.accdb" '<~~ your database path

    strConn = "Provider=Microsoft.ACE.OLEDB.12.0;" & _
        "Data Source=" & PathOfAccess & ";"
    Set cn = CreateObject("ADODB.Connection")
    cn.Open strConn

strSQL = "INSERT INTO Tabela1 (Ticker, day, open, high, low, close, vol)  select * from [" & sWs & "$] IN '' " _
  & "[Excel 8.0;HDR=yes;IMEX=2;DATABASE=" & myFn & "]"

cn.Execute strSQL
End Sub