powershell - 拆分字符串,然后分配拆分
问题描述
我有一个文本文件,在文本文件中有两个名字,一模一样。
汤姆哈迪
布拉德·皮特
我使用它从文件中获取名称并拆分它们。
$Names = gc C:\Temp\Name.txt
ForEach-Object {-Split $Names}
然后如何将每个名字分配给 $FirstName 并将每个姓氏分配给 $LastName?
这背后的想法是,更进一步,对于每个 $FirstName,我将为每个名称创建一个特定的单个项目。
我明白,在我运行上述内容之后,名称的每个部分都分配给 $_ 所以我可以对每个部分做同样的事情,即
$Names = gc C:\Temp\Name.txt
$SplitNames = ForEach-Object {-Split $Names}
ForEach ($_ in $SplitNames) {Write-Host 'Name is' $_}
Name is Tom
Name is Hardy
Name is Brad
Name is Pitt
希望这是有道理的,如果需要更多说明,请告诉我。
解决方案
# Read the input file line by line with Get-Content and send each line
# to the ForEach-Object cmdlet, which sees each line as automatic variable
# $_
Get-Content C:\Temp\Name.txt | ForEach-Object {
# Split the line into tokens by whitespace.
# * $firstName receives the 1st token,
# * $lastName the 2nd one (if there were more, $lastName would become an *array*)
$firstName, $lastName = -split $_
# Work with $firstName and $lastName
}
如果您想收集名称对以供以后使用,请考虑将它们包装在自定义对象中,如DarkLite1 的答案。
至于你尝试了什么:
ForEach-Object { -Split $Names }
ForEach ($_ in $SplitNames) {Write-Host 'Name is' $_}
如果您在ForEach-Object
没有向其提供管道输入的情况下调用,则脚本块将执行一次,因此这ForEach-Object { -Split $Names }
实际上与仅调用-Split $Names
.
通常,这些陈述表明 PowerShell 的各种枚举构造之间的区别存在混淆。
PowerShell 的各种枚举结构:
-
- 旨在通过管道接收输入(
|
) - 在自动变量中反映每个输入对象
$_
- 例如,
1, 2, 3 | ForEach-Object { "number: $_ " }
- 注意:
$null
作为输入发送确实会导致调用 - 与foreach
循环不同。
- 旨在通过管道接收输入(
foreach
循环语句:- 旨在枚举指定的内存中集合
- 通过自选迭代变量(最好不要选择
$_
,以免混淆) - 例如,
foreach ($num in 1, 2, 3) { "number: $num" }
- 注意:
$null
由于输入集合不会导致进入循环体 - 与ForEach-Object
.
PSv4+还提供
.ForEach()
数组方法:- 与
foreach
循环类似,它旨在枚举内存中的集合,但您将其作为集合本身的方法调用。 - 与
ForEach-Object
cmdlet 类似,它是反映当前迭代对象的自动变量。$_
- 它提供了附加功能,例如按名称枚举属性、执行类型转换、调用方法。
- 例如,
(1, 2, 3).ForEach({ "number: $_" }
- 注意:
$null
由于输入集合不会导致脚本块的调用 - 与ForEach-Object
.
- 与
也许令人惊讶的是,PowerShell 的
switch
语句也对碰巧是集合的输入执行枚举。switch
等价于is(注意使用foreach ($num in 1, 2, 3) { "number: $num" }
自动变量$_
作为隐式迭代器变量):
switch (1, 2, 3) { default { "number: $_"; continue } }
switch
在内存效率、性能和输出时序方面与循环语句类似foreach
,所以下面不再单独讨论。它的优点是能够使用复杂的条件以及能够通过-File
选项直接枚举文件的行。- 注意:
$null
由于输入集合确实会导致对分支的评估 - 与foreach
循环不同。
有点令人困惑的是,它foreach
也是. ForEach-Object
如果使用foreach
,则由解析上下文决定使用哪个构造:在管道中(命令上下文,参数模式),foreach
指的是ForEach-Object
cmdlet,否则它指的是foreach
循环(表达式模式) - 有关详细信息,请参阅此答案。
权衡:在以下情况下使用什么构造:
注意:以下内容侧重于类似循环的构造,但通常适用于在管道(流)中对比使用cmdlet,另一方面,与语言语句和基于运算符的表达式/方法调用进行对比。[1]
- 性能(执行速度)
foreach
循环通常最快,其次是方法.ForEach()
,ForEach-Object
cmdlet 最慢(管道通常很慢;见底部)
- 记忆效率
- 只有
ForEach-Object
cmdlet(管道)提供流处理,其中每个对象在生成时进行处理;除非将整体结果收集在内存中(例如,与输出到文件相反),否则无论最终处理多少对象,这都会使内存使用保持不变。 foreach
并.ForEach()
要求输入集合完全存在于内存中。
- 只有
- 输出时序
- cmdlet(以及一般的
ForEach-Object
管道)在处理/生成对象时传递对象,因此您通常会立即开始看到输出。 foreach
并且.ForEach()
,当直接对命令进行操作时,必须先完整地收集该命令的输出,然后才能开始枚举。
- cmdlet(以及一般的
- 语法便利和功能集
- 该
.ForEach()
方法可以按原样用作表达式的一部分,而使用ForEach-Object
需要封闭(...)
,使用foreach
循环需要封闭$(...)
- 该
.ForEach()
方法提供了允许简洁表达式的附加功能(使用foreach
and模拟功能ForEach-Object
是可能的,但更冗长)
- 该
性能对比:
运行以下使用简单循环体的性能比较命令foreach
显示比.ForEach()
我的测试更快,并且ForEach-Object
最慢,正如预期的那样。请注意,代码从这个 GistTime-Command
下载并定义函数(我可以向您保证这样做是安全的,但您应该始终自己检查源代码):
# Download and define the Time-Command function.
irm https://gist.github.com/mklement0/9e1f13978620b09ab2d15da5535d1b27/raw/Time-Command.ps1 | iex
Write-Verbose -vb "1,000 items, average of 10 runs"
$a=1..1000; Time-Command -Count 10 { foreach($e in $a) { (++$e) } }, { $a.ForEach({ (++$_) }) }, { $a | ForEach-Object { (++$_) } } | Out-Host
Write-Verbose -vb "100,000 items, average of 10 runs"
$a=1..1e5; Time-Command -Count 10 { foreach($e in $a) { (++$e) } }, { $a.ForEach({ (++$_) }) }, { $a | ForEach-Object { (++$_) } } | Out-Host
来自双核 Windows 10 VM 的结果,带有 Windows PowerShell v5.1 / PowerShell Core 7.2.0-preview.6;请注意,绝对数字并不重要,并且会根据许多变量而有所不同,但比率(列Factor
)应该让您有所了解。
Windows PowerShell v5.1:
VERBOSE: 1,000 items, average of 10 runs
Factor Secs (10-run avg.) Command TimeSpan
------ ------------------ ------- --------
1.00 0.001 foreach($e in $a) { (++$e) } 00:00:00.0013639
3.99 0.005 $a.ForEach({ (++$_) }) 00:00:00.0054464
7.46 0.010 $a | ForEach-Object { (++$_) } 00:00:00.0101785
VERBOSE: 100,000 items, average of 10 runs
Factor Secs (10-run avg.) Command TimeSpan
------ ------------------ ------- --------
1.00 0.014 foreach($e in $a) { (++$e) } 00:00:00.0144434
37.56 0.542 $a.ForEach({ (++$_) }) 00:00:00.5424872
62.61 0.904 $a | ForEach-Object { (++$_) } 00:00:00.9043278
PowerShell 核心 7.2.0-preview.6:
VERBOSE: 1,000 items, average of 10 runs
Factor Secs (10-run avg.) Command TimeSpan
------ ------------------ ------- --------
1.00 0.001 foreach($e in $a) { (++$e) } 00:00:00.0013071
4.23 0.006 $a.ForEach({ (++$_) }) 00:00:00.0055324
8.04 0.011 $a | ForEach-Object { (++$_) } 00:00:00.0105058
VERBOSE: 100,000 items, average of 10 runs
Factor Secs (10-run avg.) Command TimeSpan
------ ------------------ ------- --------
1.00 0.010 foreach($e in $a) { (++$e) } 00:00:00.0095252
34.12 0.325 $a.ForEach({ (++$_) }) 00:00:00.3250133
57.83 0.551 $a | ForEach-Object { (++$_) } 00:00:00.5508560
注意:在发现原始方法中的缺陷后,上述基准已更新。新的数字导致了不同的结论。
一般意见:
foreach
是迄今为止最快的,其次是.ForEach()
,ForEach-Object
是迄今为止最慢的- 注意:虽然这正如预期的那样,但鉴于管道引入了开销,减速在很大程度上归因于PowerShell 7.2的(and ) cmdlet 的低效实现
ForEach-Object
Where-Object
- 请参阅此博客文章以获得出色的分析,这导致了GitHub功能请求 #10982。
- 注意:虽然这正如预期的那样,但鉴于管道引入了开销,减速在很大程度上归因于PowerShell 7.2的(and ) cmdlet 的低效实现
的性能优势
foreach
随着迭代次数的增加而增长,并且在大量的情况下是相当可观的。然而,与迭代次数无关,.ForEach()
它的速度似乎大约是 的两倍ForEach-Object
。就绝对时间而言,PowerShell Core 似乎比具有大量迭代次数的 Windows PowerShell 执行得更好。
在 macOS 上(结果未在上面显示),减速因素似乎更大,并且绝对执行似乎也更慢。
在同一个 PowerShell 会话中重新运行测试会扩大性能差距,这表明只有
foreach
语句才能从按需编译中受益。
[1] 例如,对比使用cmdlet Get-Content
与使用语言语句 读取文件;或对比使用cmdlet与通过方法或运算符过滤集合。switch
-File
Where-Object
.Where()
-match
推荐阅读
- vba - 是否可以从 MS Access 或 FileMaker Pro 数据库向用户发送通知/请求?
- python - Python 3:在特定条件下拆分字符串
- java - 如何在 Amazon Lambda 中运行第三方胖 JAR?
- sql - 如何使用 SQL 查询 GAL
- asp.net-mvc - 如何将 mikrotik 路由器与 asp.net 网站集成?
- batch-file - 批处理文件增加所有文件名的最后一个数字?
- python - 自引用几个不同的对象而不是一个
- android - 预览布局中的Android Studio白屏
- xml - XML - XSLT 元素列表 - 连接具有相同属性值的元素
- c++ - `using`和直接插入的区别