如何通过PowerShell将文本文件的每一行保存为数组

30

如果我有一个文本文件,C:\USER\Documents\Collections\collection.txt,其中包含以下信息:

collectionA.json
collectionB.json
collectionC.json
collectionD.json
我想知道如何通过 Powershell,将文本文件中的每一行存储为数组元素,例如...

我在想如何通过 PowerShell 将文本文件中的每一行作为数组元素存储,例如...

array arrayFromFile = new Array;
foreach(line x in collection.txt)
{
    arrayFromFile.Add(x);
}

以以下目标为最终目的:

foreach(string x in arrayFromFile)
{
    newman run x;
}

对于这个看似简单的问题,我深表歉意——我以前从未接触过Powershell。


3
顺便提一句:你不能使用“.Add()”来扩展数组,因为数组是固定大小的列表(尽管实现了“IList”接口会有一个“.Add()”方法存在,但在运行时它会抛出异常)。 - mklement0
此外,您可以使用 $myArr = New-Object System.Collections.ArrayList 并使用相同的 .Add 方法将其添加到其中,因为它们是动态的。 - Ste
3个回答

50
Get-Content 命令从文本文件中返回每一行作为一个单独的字符串,因此将给你一个数组(只要你不使用 -Raw 参数;该参数会将所有行组合成一个单独的字符串)。
[string[]]$arrayFromFile = Get-Content -Path 'C:\USER\Documents\Collections\collection.txt'

他出色的回答中,mklement0详细介绍了调用此命令时实际发生的情况以及如果您关心性能而不是方便的备选方法。如果您有兴趣学习更多关于语言而不仅仅是解决这个单一需求,那么绝对值得一读。


38

为了补充JohnLBevan的有用回答:

Get-Content作为一个cmdlet,将输出对象逐个pipeline中,一旦可用。(请注意,即使在缺少管道符号|的情况下调用cmdlet时,也涉及管道。后者需要链接多个命令)。
在这种情况下,输出对象是输入文本文件的单个

如果你收集管道的输出对象,例如通过将其分配给变量,如$arrayFromFile或通过在使用(...)的更大表达式的上下文中使用管道:

  • PowerShell 会捕获多个输出对象,并创建一个类型为[object[]]的数组,
  • 如果只有一个输出对象,则该对象会被直接捕获 (没有数组包装)
然而,在许多情况下,PowerShell将标量(不是集合的单个值)与数组(集合)视为相同。因此,在foreach语句或将值输出以枚举到管道进行处理时(例如通过ForEach-Object cmdlet),通常不必确保始终接收数组。因此,以下命令可以正常工作,无论输入文件包含多少行:
# OK - read all lines, then process them one by one in the loop.
# (No strict need to collect the Get-Content output in a variable first.)
foreach ($line in Get-Content C:\USER\Documents\Collections\collection.txt) {
  newman run $line
}

# Alternative, using the pipeline:
# Read line by line, and pass each through the pipeline, as it is being
# read, to the ForEach-Object cmdlet.
# Note the use of automatic variable $_ to refer to the line at hand.
Get-Content C:\USER\Documents\Collections\collection.txt |
  ForEach-Object { newman run $_ }

为了确保命令的输出始终为一个数组,PowerShell提供了@(...),即数组子表达式运算符,即使是单个对象的输出也会被包装在数组中。
因此,PowerShell惯用的解决方案是:
$arrayFromFile = @(Get-Content C:\USER\Documents\Collections\collection.txt)

疯狂技术员指出,您还可以使用[array]将管道输出转换/类型限制为@(...)的替代方案,该方法也会创建[object[]]数组:

# Equivalent of the command above that additionally locks in the variable date type.
[array] $arrayFromFile = Get-Content C:\USER\Documents\Collections\collection.txt

通过使用[array] $arrayFromFile = ...而不是$arrayFromFile = [array] (...),变量$arrayFromFile变得类型约束,这意味着它的数据类型被锁定(而默认情况下,PowerShell允许您随时更改变量的类型)。 [array]是John答案中使用的类型特定转换的独立于命令的替代方法,您可以使用后者来强制使用统一类型跨越数组的元素,但在PowerShell中通常不需要[1]
常规的PowerShell数组的类型为[object[]],允许混合不同类型的元素,但是任何给定的元素仍然具有特定的类型;例如,尽管上述命令后$arrayFromFile的类型为[object[]],但例如第一个元素$arrayFromFile[0]的类型仍然是[string](假设文件至少包含1行;使用$arrayFromFile[0].GetType().Name验证类型)。

更快的替代方法:直接使用.NET框架

Cmdlets和管道提供了高级、潜在的内存限制功能,具有表达性和方便性,但它们可能会很

当性能很重要时,必须直接使用.NET框架类型,例如在这种情况下使用[System.IO.File]

$arrayFromFile = [IO.File]::ReadAllLines('C:\USER\Documents\Collections\collection.txt')

请注意类型名称中可以省略System.前缀。

  • 与John的答案一样,这将返回一个[string[]]数组。

  • 注意事项:

    • 小心使用相对路径,因为.NET通常有一个不同的当前目录,而PowerShell则不同;为了解决这个问题,始终传递绝对路径,在最简单的情况下,例如使用"$PWD/collection.txt",最可靠的方式是
      "$((Get-Location -PSProvider FileSystem).ProviderPath)/collection.txt"

    • .NET的默认编码是UTF-8,而Windows PowerShell默认为“ANSI”编码,即系统区域设置的传统代码页;PowerShell Core (v6+)与此相反,也默认为UTF-8。使用Get-Encoding-Encoding参数或接受编码实例的.ReadAllLines()重载来明确指定输入文件的字符编码。


[1] 通常情况下,PowerShell的隐式运行时类型转换无法像C#一样提供相同的类型安全性。例如,[string[]] $a = 'one', 'two'; $a[0] = 42并不会引起错误:PowerShell会静默地将[int]42转换为字符串。


2
非常感谢您的出色回答。我已经测试了.NET方法,它比Get-Content快了10倍以上。113k行代码用.NET方法只需要0.0933256秒,而使用Get-Content则需要1.1153968秒。 - Ste

6
$array = Get-Content -Path @("C:\tmp\sample.txt")
foreach($item in $array)
{
 write-host $item 
} 


2
展示一个完整的例子是个好主意。请注意,在-Path参数周围不需要使用@(...)-Path C:\tmp\sample.txt就可以了。在PowerShell中,数组值参数始终也接受标量值(即使有多个值,例如-Path sample1.txt, sample2.txt也可以)。 - mklement0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接