分割字符串,然后分配分割

时间:2018-08-06 13:53:40

标签: 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

希望这是有道理的,如果需要更多说明,请告诉我。

3 个答案:

答案 0 :(得分:5)

# 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's answer所示。


关于您尝试过的事情

  

ForEach-Object { -Split $Names }

     

ForEach ($_ in $SplitNames) {Write-Host 'Name is' $_}

如果在未提供流水线输入的情况下调用ForEach-Object,则脚本块将被执行一次 ,因此ForEach-Object { -Split $Names }实际上与仅调用{{1} }。

通常,这些陈述表明,在 PowerShell枚举构造之间的区别上存在困惑

  • ForEach-Object cmdlet

    • 旨在通过管道-Split $Names
    • 接收输入
    • 自动变量|
    • 中反映每个输入对象
    • 例如$_
  • foreach loop statement

    • 旨在枚举指定的内存中集合
    • 通过自选迭代变量(最好不要选择1, 2, 3 | ForEach-Object { "number: $_ " },以免引起混淆)
    • 例如$_
  • PSv4 + 还提供了 .ForEach() method

    • 类似于foreach ($num in 1, 2, 3) { "number: $num" }循环,它旨在枚举内存中的集合 ,但是您 invoke it as a method集合本身。
    • 类似于foreach cmdlet,它是自动变量ForEach-Object ,它反映了当前迭代的对象。
    • 提供了其他功能,例如按名称枚举属性,执行类型转换,调用方法。
    • 例如$_

也许令人困惑,(1, 2, 3).ForEach({ "number: $_" }还是foreach的内置别名。如果使用ForEach-Object,则上下文将确定所使用的构造:在管道(命令上下文,参数模式)中,foreach引用foreach cmdlet,否则引用ForEach-Object 循环(表示模式)。 [1]

关于何时使用哪种构造:在以下之间需要权衡:

  • 性能(执行速度)
    • foreach循环通常最快,其次是foreach方法,其中.ForEach() cmdlet最慢(管道通常很慢) [2] < / sup>
  • 内存效率
    • ForEach-Object cmdlet(管道)提供处理,其中每个对象在生成时都进行处理;除非整体结果收集在内存中(例如,与输出到文件相反),否则此 keeps内存将使用常量,而不管最终要处理多少个对象。
    • ForEach-Objectforeach要求输入集合必须完整存在于内存中
  • 输出时间
    • .ForEach() cmdlet(通常是管道)在处理/生产对象时将其传递给对象,因此通常您会立即开始看到输出。
    • ForEach-Objectforeach,在给出命令的输出时,必须在开始枚举之前先完整地收集该命令的输出。
  • 便利
    • .ForEach()方法可以按原样用作表达式的一部分,而.ForEach()的使用要求将ForEach-Object括起来并使用{{ 1}}循环需要包含(...)
    • foreach方法提供了允许简洁表达的附加功能(可以使用$(...).ForEach()来模拟该功能,但更冗长)

[1]通过运行Get-Help about_Parsing,您可以详细了解PowerShell的两种基本解析模式,即参数模式(类似于命令)和表达式模式。 / p>

[2]运行以下使用微不足道的循环主体的性能比较命令,显示foreach在测试中比ForEach-Object快,而foreach则最慢。您可以从this Gist下载.ForEach()脚本:

ForEach-Object

在笔记本电脑上运行Windows PowerShell v5.1 / PowerShell Core 6.1.0-preview.4的单核Windows 10 VM的结果;请注意,绝对数字并不重要,并且会根据许多变量而有所不同,但是 ratio (列Test-Command.ps1)应该会给您一个感觉。
如果您获得不同的排名,请告诉我们。

Windows PowerShell v5.1:

100个项目,平均运行10,000次:

# 100 items, average of 10,000 runs
$a=1..100; ./Time-Command { foreach($e in $a) { ++$e } }, { $a.ForEach({ ++$_ }) }, { $a | ForEach-Object { ++$_ } } 10000
# 1 million items, average of 10 runs
$a=1..1e6; ./Time-Command { foreach($e in $a) { ++$e } }, { $a.ForEach({ ++$_ }) }, { $a | ForEach-Object { ++$_ } } 10

1百万个项目,平均10次运行:

Factor

PowerShell Core 6.1.0-preview.4:

100个项目,平均运行10,000次:

Command                        FriendlySecs (10000-run avg.) TimeSpan         Factor
-------                        ----------------------------- --------         ------
 foreach($e in $a) { ++$e }    0.000                         00:00:00.0001415 1.00
 $a.ForEach({ ++$_ })          0.000                         00:00:00.0002937 2.08
 $a | ForEach-Object { ++$_ }  0.001                         00:00:00.0006257 4.42

1百万个项目,平均10次运行:

Command                        FriendlySecs (10-run avg.) TimeSpan         Factor
-------                        -------------------------- --------         ------
 foreach($e in $a) { ++$e }    1.297                      00:00:01.2969321 1.00
 $a.ForEach({ ++$_ })          2.548                      00:00:02.5480889 1.96
 $a | ForEach-Object { ++$_ }  5.992                      00:00:05.9917937 4.62

一般观察:

  • 在100个项目和100万个项目之间的相对性能变化不大:Command FriendlySecs (10000-run avg.) TimeSpan Factor ------- ----------------------------- -------- ------ foreach($e in $a) { ++$e } 0.000 00:00:00.0001981 1.00 $a.ForEach({ ++$_ }) 0.000 00:00:00.0004406 2.22 $a | ForEach-Object { ++$_ } 0.001 00:00:00.0008829 4.46 大约是Command FriendlySecs (10-run avg.) TimeSpan Factor ------- -------------------------- -------- ------ foreach($e in $a) { ++$e } 1.741 00:00:01.7412378 1.00 $a.ForEach({ ++$_ }) 4.099 00:00:04.0987548 2.35 $a | ForEach-Object { ++$_ } 8.119 00:00:08.1188042 4.66 的2倍和4-5倍比foreach快。

  • 在这些测试中,Windows PowerShell明显比PS Core快,而macOS和Linux上的PS Core似乎更慢。

答案 1 :(得分:3)

Split会在拆分每个位置后为您提供一个包含内容的数组。 您可以寻址数组中的每个条目,然后用于其他目的:

例如:("Tom Hardy" -split " ")[0] = Tom

$Names = gc C:\Temp\Name.txt

foreach ($name in $Names)
{
  $cutted = $name.Split()
  $firstname = $cutted[0]
  $lastname = $cutted[1]
  #Do whatever you need to do with the names here
}

如@iRon所述,您实际上可以跳过一个步骤,并将其直接从拆分中保存到两个变量中:

$Names = gc C:\Temp\Name.txt

foreach ($name in $Names)
{
  $firstname, $lastname = $name -split " "
  #Do whatever you need to do with the names here
}

或者作为一个单行:

Get-Content -Path "C:\Temp\Name.txt" | % {$firstname, $lastname = $_ -split " "; #Do something with the variables}

答案 2 :(得分:3)

与@Paxz相同,但有一些解释和建议:

$Names = @(
    'Brad Pitt',
    'Tom Hardy',
    'Daniel Craig Junior'
)

# the .ForEAch method is used as it's faster then piping data to Foreach-Object
$result = $Names.ForEach({
    # we use the second argument of -Split to indicate 
    # we're only interested in two values
    $tmpSplit = $_ -split ' ', 2

    # we then create an object that allows us to 
    # name things propertly so we can play with it later withoout hassle
    [PSCustomObject]@{
        Input = $_
        FirstName = $tmpSplit[0]
        LastName = $tmpSplit[1]
    }
})

# here we show the result of all our objects created
$result

# enable verbose text to he displayed
$VerbosePreference = 'Continue'

$result.ForEach({
    # here we can easily address the object by its property names
    Write-Verbose "Input '$($_.Input)' FirstName '$($_.FirstName)' LastName '$($_.LastName)'"
})

# disable verbose messages, because we don't need this in production
$VerbosePreference = 'SilentlyContinue'