有很多方法可以使用字符串中的变量。我正在调用这个变量替换,但我指的是要尝试格式化字符串以包含变量的值。这是一个很多东西,我发现自己经常向新脚本运动员解释。

指数

级联

第一类方法可以称为倾斜。它基本上是几个弦并在一起加入它们。使用这一段历史悠久地建立形成的字符串。

$name = 'Kevin Marquette'
$message = 'Hello, ' + $name

当添加少数值要添加时,这会解决问题。特别是如果字符串末尾只有一个值。但这可以快速螺旋变得复杂。

$first = 'Kevin'
$last = 'Marquette'

$message = 'Hello, ' + $first + ' ' + $last + '.'

这是一个非常简单和常见的要求,这已经越来越难以阅读。

可变替代

电源外壳 有另一种选择非常容易。您可以直接在字符串中指定变量。

$message = "Hello, $first $last."

这是您在字符串上使用的引号类型的位置会有所不同。双引号字符串将允许替换,但单引号字符串不会。有时候你想要一个或另一个,所以你有一个选择。

命令替代

当您开始尝试将属性值达到字符串时,事情会变得有点棘手。这是许多新人被绊倒的地方。首先让我向你展示他们认为应该工作的内容(并且在面值几乎看起来应该是这样)。

$directory = Get-Item 'c:\windows'
$message = "Time: $directory.CreationTime"

You would be expecting to get the CreationTime off of the $directory, but instead you get this Time: c:\windows.CreationTime as your value. The reason is that this type of substitution only sees the base variable. It considers the period as part of the string so it stops resolving the value any deeper.

It just so happens that this object gives a string as a default value when placed into a string. Some objects will give you the type name instead like System.Collections.Hashtable. Just something to watch for.

电源外壳 允许您使用特殊语法在字符串内执行命令执行。这允许我们获取这些对象的属性并运行任何其他命令以获得值。

$message = "Time: $($directory.CreationTime)"

这对某些情况有利,但如果您只有几个变量,它可以像连接一样疯狂。

命令执行

我很快就在这一点上釉了。您可以在字符串中运行命令。

$message = "Date: $(Get-Date)"

即使我有这个选项,我也不喜欢它。它迅速变得杂乱,难以调试。我将运行命令并保存到变量或使用格式字符串。

格式字符串

.NET有一种方法来格式化字符串,我发现要相当容易使用。首先让我向你展示它,因为我向你展示了Powershell快捷方式做同样的事情。

# .Net string format string
[string]::Format('Hello, {0} {1}.',$first,$last)

# Powershell format string
'Hello, {0} {1}.' -f $first, $last

What is happening here is that the string is parsed for the tokens {0} and {1}, then it uses that number to pick from the values provided. If you want to repeat one value some place in the string, you can reuse that values number.

字符串变得更复杂,您将取出这种方法的价值越多。

将值格式为数组

如果您的格式线路变得太长,则可以先将值放入数组。

$values = @(
    "Kevin"
    "Marquette"
)
'Hello, {0} {1}.' -f $values

这不是拆分,因为我通过整个阵列,但这个想法是相似的。

高级格式化

我故意从.NET叫出这些,因为有很多格式化选项已经很好 记录 在上面。以方法构建格式化各种数据类型。

"{0:yyyyMMdd}" -f (get-date)
"Population {0:N0}" -f  8175133

我不会进入他们,但我只是想让你知道这是一个非常强大的格式引擎,如果你需要它。

加入字符串

Sometimes you actually do want to concatenate a list of values together. There is a -Join operator that can do that for you. It will even let you specify a character to join between the strings.

$servers = @(
    'server1'
    'server2'
    'server3'
)

$servers  -join ','

If you want to -Join some strings without a separator, you need to specify an empty string ''. But if that is all you need, there is a faster option.

[string]::Concat('server1','server2','server3')
[string]::Concat($servers)

It is also worth pointing out that you can also -Split strings too.

加入路径

这通常被忽视,而是一个伟大的cmdlet来构建文件路径。

$folder = 'Temp'
Join-Path -Path 'c:\windows' -ChildPath $folder

关于此的伟大事物是在将值放在一起时正确地完成反斜杠。如果您从用户或配置文件中占用值,则尤其重要。

This also goes well with Split-Path and Test-Path. I also cover these in my post about 读取和保存到文件.

字符串是阵列

在我继续之前,我确实需要在这里添加字符串。请记住,字符串只是一个字符数组。将多个字符串添加在一起时,每次都会创建一个新的数组。

看这个例子:

$message = "Numbers: "
foreach($number in 1..10000)
{
    $message += " $number"
}

It looks very basic but what you don’t see is that each time a string is added to $message that a whole new string is created. Memory gets allocated, data gets copied and the old one is discarded. Not a big deal when it is only done a few times, but a loop like this would really expose the issue.

StringBuilder.

StringBuilder. 也非常流行,可以从大量较小的弦中构建大字符串。原因是因为它只收集到它的所有字符串,并且在检索值时只能在最后连接它们。

$stringBuilder = New-Object -TypeName "System.Text.StringBuilder"

[void]$stringBuilder.Append("Numbers: ")
foreach($number in 1..10000)
{
    [void]$stringBuilder.Append(" $number")
}
$message = $stringBuilder.ToString()

同样,这是我达到的.NET。我不想再使用它,但很高兴知道它是在那里。

用牙套描绘

这用于字符串中的后缀连接。有时,您的变量没有干净的单词边界。

$test = "Bet"
$tester = "Better"
Write-Host "$test $tester ${test}ter"

谢谢 / u / real_parbold for that one.

这是这种方法的交替:

Write-Host "$test $tester $($test)ter"
Write-Host "{0} {1} {0}ter" -f $test, $tester

我个人使用格式字符串,但是很高兴知道Incase你在野外看到它。

查找并替换令牌

虽然大多数竞争对手限制了您需要滚动自己的解决方案,但有时您可以在其中有大量的模板文件,您可以在其中替换里面的字符串。

让我们假设您从有很多文本的文件中删除了模板。

$letter = Get-Content -Path TemplateLetter.txt -RAW
$letter = $letter -replace '#FULL_NAME#', 'Kevin Marquette'

你可能有很多令牌来替换。诀窍是使用一个非常独特的令牌,很容易找到和更换。我倾向于在两端使用特殊的角色来帮助区分它。

我最近找到了一种方法来解决这个问题。我决定在这里留下这个部分,因为这是一个常用的模式。

替换多个令牌

当我有一个我需要替换的令牌列表时,我采取了更通用的方法。我会把它们放在一个哈希表中并迭代它们来完成替换。

$tokenList = @{
    Full_Name = 'Kevin Marquette'
    Location = 'Orange County'
    State = 'CA'
}

$letter = Get-Content -Path TemplateLetter.txt -RAW
foreach( $token in $tokenList.GetEnumerator() )
{
    $pattern = '#{0}#' -f $token.key
    $letter = $letter -replace $pattern, $token.Value
}

如果需要,可以从JSON或CSV加载这些令牌。

ExecutionContext展开

有一个聪明的方法可以使用单引号定义替换字符串,并稍后展开“加利线”。看这个例子:

$message = 'Hello, $Name!'
$name = 'Kevin Marquette'
$string = $ExecutionContext.InvokeCommand.ExpandString($message)

The call to .InvokeCommand.ExpandString on the current execution context will use the variables in the current scope for substitution. The key thing here is that the $message can be defined very early before the variables even exist.

如果我们在那样扩展一点,我们可以在不同的值上遍及和过度地执行此替换。

$message = 'Hello, $Name!'
$nameList = 'Mark Kraus','Kevin Marquette','Lee Dailey'
foreach($name in $nameList){
    $ExecutionContext.InvokeCommand.ExpandString($message)
}

继续继续这个想法;您可以从文本文件导入大型电子邮件模板以执行此操作。我要感谢 马克克劳斯 为了这 避免 .

无论什么适合你

我是格式字符串方法的粉丝。我绝对用更复杂的字符串或有多个变量来执行此操作。关于任何非常短的东西,我可以使用其中任何一个。

还要别的吗?

我在这个覆盖了很多地面。我的希望是你走开了倾向于新的东西。

这是我们涵盖的一切列表,以防您想要跳回某些东西。

指数