精通 PowerShell 中的 Import-Csv 和其他 CSV 命令

PowerShell的Export-Csv cmdlet和PowerShell的Import-Csv cmdlet允許管理員通過foreach循環導入CSV,使用Export-Csv來追加CSV並將數組導出到CSV文件等等。

在本文中,您將了解到許多常見情境,您可以使用PowerShell來管理CSV文件,例如:

  • 通過PowerShell讀取CSV文件
  • 通過PowerShell保存CSV文件
  • 在運行Export-CSV之前格式化輸出
  • 追加到CSV文件
  • 將差異追加到現有文件
  • Export-CSV#TYPE字符串

如果您對CSV文件的內部運作方式不熟悉,它們是遵循一個標準格式的文本文件,該格式在表格中以逗號分隔值。您可以使用Export-Csv cmdlet在PowerShell中創建CSV文件,並將一個或多個對象通過管道傳遞給它。

下面的命令查找前兩個運行的進程並將生成的對象傳遞給Export-Csv cmdlet。然後,Export-Csv cmdlet在系統驅動器的根目錄(很可能是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。這兩個命令實際上以相同的方式讀取文件。但是Import-Csv更進一步。Import-Csv了解不僅僅是文本文件,還了解CSV文件的內部結構。

由於CSV文件遵循特定的結構,Import-Csv了解CSV的結構。這個命令不僅從磁盤讀取文本文件,還將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命令将把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-* cmdlet時,您正在更改Export-Csv接收的輸入。這將影響保存到新CSV文件中的輸出。

如果要將輸出管道到Export-Csv cmdlet,請不要將輸出管道到任何Format-* cmdlet。

請記住,CSV文件關注的是數據,而不是格式。

附加到CSV文件

有時您可能有一個現有文件,您希望對其進行添加,而不是完全創建一個新文件。默認情況下,Export-Csv將覆蓋通過Path參數指定的任何文件。

如果需要將數據附加到CSV中,請使用Append參數。

假設您有一個循環,您希望將每個處理過的對象保存到CSV中。對於每次迭代,您都有一個不同的對象,您希望將其保存到CSV文件中。如果您不使用Append參數,每次調用Export-Csv時,它都會覆蓋該CSV文件。如果沒有Append參數,您只會得到最後一個對象,這在大多數情況下都不是所需的輸出。

下面的示例是查找前五個運行中的進程。然後,它進入PowerShell的Import-Csvforeach循環,並將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文件

使用Export-Csv僅追加屬性差異到現有的CSV文件是可能的。這意味著如果CSV行的列和要記錄的對象不同,那麼可以追加它們。

要僅追加差異到CSV文件,您需要一起使用AppendForce參數。根據Microsoft的文檔,“當結合使用ForceAppend參數時,包含不匹配屬性的對象可以寫入CSV文件。只有匹配的屬性被寫入文件,不匹配的屬性被丟棄。”

為了演示,創建一個具有兩個屬性的對象; NameAge

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

現在創建另一個具有 NameZip 屬性的對象。

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

每個對象都有不同的屬性。

接下來,從第一個對象創建一個 CSV 文件,然後嘗試在不使用 Force 參數的情況下將第二個對象附加到 CSV 文件中。你將收到一個錯誤。

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/