掌握PowerShell中的Import-Csv和其他CSV命令

PowerShell 的 Export-Csv 命令和 PowerShell 的 Import-Csv 命令允许管理员通过 foreach 循环导入 CSV 文件,使用 Export-Csv 来追加 CSV 文件并将数组导出到 CSV 文件,还有更多功能。

在本文中,您将学习到许多常见的情景,可以使用 PowerShell 来管理 CSV 文件,比如:

  • 使用 PowerShell 读取 CSV 文件
  • 使用 PowerShell 保存 CSV 文件
  • 在运行 Export-CSV 之前格式化输出
  • 追加到 CSV 文件
  • 将差异追加到现有文件
  • Export-CSV#TYPE 字符串

如果您不熟悉 CSV 文件的内部工作原理,它们是遵循标准格式的文本文件,表中的值通过逗号分隔。您可以使用 Export-Csv 命令在 PowerShell 中创建 CSV 文件,并将一个或多个对象传递给它。

下面的命令查找前两个正在运行的进程,并将生成的对象传递给 Export-Csv 命令。然后,Export-Csv 命令在系统驱动器的根目录(很可能是 C:\)中创建名为 processes.csv 的 CSV 文件。

PS51> Get-Process | Select-Object -First 2 | Export-CSV -Path "$env:SystemDrive\processes.csv"

现在使用记事本打开processes.csv。您应该在顶部看到"Name","SI","Handles","VM"作为标题。您还会看到#TYPE System.Diagnostics.Process,目前可能并不太明白。别担心,我们将在这篇文章中解释这个字符串。

使用Import-Csv读取CSV文件

想要更多类似的技巧吗?查看我的个人PowerShell博客

PowerShell有几个命令可以让您读取文本文件。这些命令是Get-ContentImport-Csv。这两个cmdlet在技术上以相同的方式读取文件。但是Import-Csv更进一步。Import-Csv了解不仅是文本文件而且是CSV文件的底层结构。

由于CSV文件遵循特定的模式,Import-Csv了解CSV模式。此cmdlet不仅从磁盘读取文本文件,还将CSV文件中的行转换为PowerShell对象。

非CSV文件

通常,CSV的数据是由逗号分隔的,但有时CSV(在那时技术上不是CSV)的数据是用不同的分隔符分隔的。这些分隔符有时可能是制表符或分号。

如果您有使用不同分隔符的 CSV 文件,您可以使用 Delimiter 参数。该参数告诉 Import-Csv 不要查找逗号,这是默认的,而是要查找另一个值。

例如,如果您有一个制表符分隔的文件,您可以像下面这样读取文件:

PS51> Import-Csv -Path tab-separated-data.csv -Delimiter "`t"

添加标题

A common Import-Csv parameter is Header. This parameter lets you specify the property names of the objects created by this cmdlet.

默认情况下,Import-Csv cmdlet 会将 CSV 文件的顶部行视为标题。然后,它会将这些值转换为每行(对象)的属性。但是,如果您有一个没有标题行的 CSV 文件,您可以使用 Header 参数来自定义一个标题。

Header 参数会阻止 Import-CSV 使用您的第一行作为标题,还会为您省去手动打开 CSV 文件以自己添加标题的麻烦。

为了演示这种行为,打开记事本并复制/粘贴下面的文本。这段文本将代表一个包含三行两列的数据集。

a,1
b,2
c,3

将文本文件保存为 test.csv。不要忘记启用文件类型扩展名,或者用双引号括起文件,以免意外将其保存为以 .csv.txt 结尾的文件!

现在,使用 Import-CSV 读取最近创建的 CSV 文件,不带 Header 参数,并检查输出。

PS51> Import-Csv .\test.csv

a 1
---
b 2
c 3

请注意,它将第一行用作对象属性。 A1 不是你想要用作对象属性的“标签”。 Abc 是字母,而 123 是数字。你需要像下面这样使用 Header 参数来定义它们:

PS51> Import-Csv .\test.csv -Header "Letter", "Number"

Letter Number
------ ------
a      1
b      2
c      3

使用 PowerShell 保存 CSV 文件

如果你需要从 PowerShell 对象创建或保存 CSV 文件,也可以采取另一种方式。虽然 Import-Csv 命令将 CSV 文件“转换”为 PowerShell 对象,Export-Csv 则相反。这个命令将 PowerShell 对象“转换”为 CSV 文件。

通过使用 Export-Csv 保存 CSV 文件,你可以稍后查看或在其他系统中使用该数据。

例如,我可以通过将 Get-Process 管道传递给 Export-Csv 命令来保存计算机上所有正在运行的进程。

PS51> Get-Process | Export-Csv -Path processes.csv

Export-Csv 命令本质上很简单,但是有一些需要注意的地方。

在运行 Export-CSV 之前格式化输出

如你所见,Export-Csv 本身进行“原始”转换。它不会添加任何特殊的富文本格式,也不会添加任何颜色等。

最常见的问题之一是在导出到 CSV 之前尝试使输出看起来更漂亮。许多用户在导出到 CSV 之前会尝试使输出看起来更好。但是我将向你展示这样做会使事情变得更糟。

你可以在 Microsoft Excel 中打开 CSV 文件并施加斜体、加粗、添加颜色等等,但如果将文件保存为 CSV 格式(而不是 Excel 工作簿),所有格式将被删除。CSV 文件简单来说并不足够“智能”

。请记住,CSV 文件只是由逗号(或有时是制表符)分隔的纯文本文件。Import-Csv 管道传递给 Format-* PowerShell cmdlet 将无法按预期工作。

根据 Microsoft Export-Csv 文档:“在将对象发送到 Export-CSV cmdlet 之前,请勿格式化对象。如果 Export-CSV 收到格式化的对象,则 CSV 文件将包含格式属性而不是对象属性。”

为什么会这样呢?

打开 PowerShell 窗口并创建一些虚拟数据。让我们使用本文第一部分介绍的 Get-Process 示例。但这次,将输出分配给一个变量。然后,将这些进程对象传递给 Format-Table cmdlet。

PS51> $a = Get-Process
PS51> $a | Format-Table
Using Format-Table

使用 Format-Table,你现在可以看到一个清晰的表格输出。

现在将此输出保存到另一个变量中。

PS51> $b = $a | Format-Table

现在使用 Get-Member 查看 $a$b 变量值的属性。这个 cmdlet 将帮助你理解为什么这两个看似相似的对象不以相同的方式导出到 CSV 文件:

PS51> $a | Get-Member
System.Diagnostics.Process object type
PS51> $b | Get-Member
Format-Table‘s many object types

Get-Process 的直接输出返回:TypeName: System.Diagnostics.Process,而Format-Table 的输出完全不同。它返回许多不同类型的对象,具有不同的属性。

如果在控制台中查看$a$b,输出看起来是相同的。这种行为是由于PowerShell的格式化系统。

这如何影响Export-Csv 的输出?

PS51> $b | Export-Csv | Format-Table

Export-Csv 将每个对象视为原样读取。当您将输出管道传递给Format-* 命令时,您正在更改Export-CSV 收到的输入。这然后影响保存到新CSV文件中的输出。

如果要将输出管道传递给Export-Csv 命令,不要将输出管道传递给任何Format-* 命令。

请记住,CSV 文件专注于数据,而不是格式。

追加到CSV文件

有时,您可能希望添加到现有文件,而不是完全创建一个新文件。默认情况下,Export-Csv 将覆盖通过Path 参数指定的任何文件。

如果需要将数据追加到CSV中,请使用Append 参数。

让我们说你有一个循环,你想要将每个处理过的对象保存到CSV中。对于每次迭代,你有一个不同的对象想要保存到CSV文件中。由于你重复调用Export-Csv,如果你不使用Append参数,它会覆盖CSV文件。没有Append参数,你将只得到最后一个对象,这在大多数情况下不是期望的输出。

下面的例子是查找前五个正在运行的进程。然后,它进入一个PowerShell Import-Csv foreach 循环,并将NameProductVersion属性逐个记录到test.csv文件中。

Get-Process | Select-Object -First 5 | Foreach-Object {
    $_ | Select-Object Name, ProductVersion | Export-CSV -Path C:\test.csv -Append
}

如果不使用Append参数,你会发现CSV文件中只会显示第五个进程。

追加差异到CSV文件

可以只将属性差异追加到现有的CSV文件中,使用Export-Csv。这意味着如果CSV行的列和要记录的对象不同,那么可以追加它们。

要仅追加差异到CSV文件,你需要同时使用AppendForce参数。根据Microsoft文档的说法:“当ForceAppend参数结合使用时,包含不匹配属性的对象可以被写入CSV文件。仅写入匹配的属性到文件中,不匹配的属性被丢弃。”

展示一下,创建一个具有两个属性的对象;NameAge

PS51> $Content = [PSCustomObject]@{Name = "Johnny"; Age = "18"}

现在创建另一个具有 NameZip 属性的对象。

PS51> $OtherContent = [PSCustomObject]@{Name = "Joe"; Zip = "02195"}

每个对象具有不同的属性。

接下来,从第一个对象创建一个 CSV 文件,然后尝试在 CSV 中不使用 Force 参数追加第二个对象。你将收到一个错误。

PS51> $Content | Export-CSV -Path .\User.csv -NoTypeInformation
PS51> $OtherContent | Export-CSV -Path .\User.csv -NoTypeInformation -Append
Export-Csv without Force

然而,如果你使用 Force 参数,Export-Csv 就会很好地工作。

PS51> $OtherContent | Export-CSV -Path .\User.csv -NoTypeInformation -Append -Force

然而,你会注意到在 CSV 文件中 Zip 列已经消失了。要谨慎使用 Force。它可能不会提供预期的输出。

PS51> Import-Csv -Path .\User.csv

Name   Age
----   ---
Johnny 18
Joe

Export-CSV#TYPE 字符串

默认情况下,使用 Export-CSV 而不带额外参数会在你的 CSV 文件顶部包含一个 #TYPE 字符串。然后这个字符串后面跟着 Export-Csv 接收到的对象类型。

#TYPE System.Diagnostics.Process

大多数情况下,这个字符串在消耗输出时实际上是没有用的。这个字符串只是在你需要保持属性和值来自的对象类型时才会存在。

要移除这个字符串,使用 NoTypeInformation 参数。此参数会从 CSV 中完全删除这个字符串。

注意,截至 PowerShell Core,这已经不再必要。

总结

使用Import-CSVExport-CSV PowerShell 命令让您轻松处理 CSV 文件。这是两个有用的命令,您在处理对象和 CSV 文件时应经常使用。

我希望通过我在这里展示的解释和示例,您能够清楚地了解何时可以利用这些命令来使自己受益。

想要更多这样的技巧吗?查看我的个人 PowerShell 博客

Source:
https://adamtheautomator.com/import-csv/