可通过多种方式在字符串中使用变量。 我调用此变量替换,但每当你想要设置字符串格式以包含变量中的值时,我都指的是。 这是我经常向新脚本师解释的一些内容。
注释
本文的 原始版本 出现在 @KevinMarquette撰写的博客上。 PowerShell 团队感谢 Kevin 与我们共享此内容。 请在 PowerShellExplained.com查看他的博客。
串联
第一类方法可以称为串联。 它基本上采用多个字符串并将其联接在一起。 使用串联生成格式化字符串有很长的历史记录。
$name = 'Kevin Marquette'
$message = 'Hello, ' + $name
如果只有几个值要添加,则连接会正常。 但这可能会很快变得复杂。
$first = 'Kevin'
$last = 'Marquette'
$message = 'Hello, ' + $first + ' ' + $last + '.'
这个简单的示例已经变得难以阅读。
变量替换
PowerShell 有另一种更简单的选项。 可以直接在字符串中指定变量。
$message = "Hello, $first $last."
围绕字符串使用的引号类型会有所差异。 双引号字符串允许替换,但单个带引号的字符串不允许。 有时你想要一个或另一个,所以你有一个选项。
命令替换
当你开始尝试将属性的值获取到字符串中时,事情会变得有点棘手。 这是许多新人被绊倒的地方。 首先,让我向你展示他们认为应该做什么(而且面值几乎看起来应该这样)。
$directory = Get-Item 'C:\windows'
$message = "Time: $directory.CreationTime"
你会期望得到 CreationTime 的 $directory,但你得到这个 Time: C:\windows.CreationTime 作为你的值。 原因是这种类型的替换只看到基变量。 它将句点视为字符串的一部分,以便它停止解析任何更深层的值。
恰好如此,此对象在放入字符串时将字符串作为默认值提供。
某些对象提供类型名称,如下所示 System.Collections.Hashtable。 只是一些值得关注的事情。
借助 PowerShell,可以使用特殊语法在字符串中执行命令。 这样,我们就可以获取这些对象的属性,并运行任何其他命令来获取值。
$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
此处发生的事情是,将分析令牌{0}{1}的字符串,然后使用该数字从提供的值中进行选择。 如果要在字符串中某个位置重复一个值,则可以重复使用该值编号。
字符串越复杂,从此方法中得到的值越多。
将值格式化为数组
如果格式行过长,则可以先将值放入数组中。
$values = @(
"Kevin"
"Marquette"
)
'Hello, {0} {1}.' -f $values
这不是喷洒,因为我传入了整个数组,但这个想法是类似的。
高级格式设置
我故意将这些选项称为来自 .NET,因为有很多格式设置选项 已经记录在 它上。 可通过内置方式设置各种数据类型的格式。
"{0:yyyyMMdd}" -f (Get-Date)
"Population {0:N0}" -f 8175133
20211110
Population 8,175,133
我不会进入他们, 但我只是想让你知道, 这是一个非常强大的格式引擎, 如果你需要它。
联接字符串
有时你确实想要将值列表连接在一起。 有一个 -join 运算符可以为你执行此作。 它甚至可以指定要在字符串之间联接的字符。
$servers = @(
'server1'
'server2'
'server3'
)
$servers -join ','
如果想要 -join 一些没有分隔符的字符串,则需要指定空字符串 ''。
但是,如果这是你所需要的一切,有一个更快的选择。
[string]::Concat('server1','server2','server3')
[string]::Concat($servers)
这也值得指出,也可以 -split 字符串。
Join-Path
这通常被忽略,但用于生成文件路径的出色 cmdlet。
$folder = 'Temp'
Join-Path -Path 'C:\windows' -ChildPath $folder
这一点的一件大事是,它把值放在一起时,它正确地使用了反斜杠。 如果从用户或配置文件获取值,则这一点尤其重要。
这也与 Split-Path 和 Test-Path。 我还在帖子中介绍了 有关阅读和保存到文件的这些内容。
字符串是数组
在继续作之前,我确实需要在此处提及添加字符串。 请记住,字符串只是字符数组。 在一起添加多个字符串时,每次都会创建一个新数组。
查看此示例:
$message = "Numbers: "
foreach($number in 1..10000)
{
$message += " $number"
}
它看起来非常基本,但你看不到的是,每次将字符串添加到 $message 该字符串时,都会创建一个新的字符串。 分配内存、复制数据并丢弃旧内存。
当它只做了几次时,这并不大不了,但这样的循环真的会暴露问题。
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 那一个。
下面是此方法的替代方法:
Write-Host "$test $tester $($test)ter"
Write-Host "{0} {1} {0}ter" -f $test, $tester
我个人对此使用格式字符串,但这是众所周知的,以防你在野外看到它。
查找和替换令牌
虽然其中大多数功能限制了需要滚动自己的解决方案,但有时你可能有大型模板文件,你希望在其中替换字符串。
假设你从包含大量文本的文件中提取了模板。
$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 ExpandString
使用单引号定义替换字符串并稍后展开变量有一种巧妙的方法。 查看此示例:
$message = 'Hello, $Name!'
$name = 'Kevin Marquette'
$string = $ExecutionContext.InvokeCommand.ExpandString($message)
对当前执行上下文的调用 InvokeCommand.ExpandString 使用当前作用域中的变量进行替换。 此处的关键是, $message 可以在变量存在之前非常早地定义。
如果我们稍微扩展一点,则可以使用不同的值执行此替换。
$message = 'Hello, $Name!'
$nameList = 'Mark Kraus','Kevin Marquette','Lee Dailey'
foreach($name in $nameList){
$ExecutionContext.InvokeCommand.ExpandString($message)
}
继续这个想法:可以从文本文件导入大型电子邮件模板以执行此作。 我必须感谢 马克·克劳斯 的这个建议。
无论哪种方案最适合你
我是格式字符串方法的粉丝。 我肯定使用更复杂的字符串执行此作,或者如果有多个变量。 在非常短的任何内容上,我可以使用其中任何一个。
别的东西?
我在这块上面覆盖了很多地面。 我希望你离开学习一些新的东西。
链接
若要详细了解使字符串内插成为可能的方法和功能,请参阅以下列表以获取参考文档。
- 串联使用 加法运算符
- 变量和命令替换遵循 引用规则
- 格式设置使用 格式运算符
- 联接字符串使用 联接运算符 和引用
Join-Path,但你也可以阅读以下内容Join-String - 数组记录在 “关于”数组中
- StringBuilder 是一个 .NET 类,其 自己的文档
- 引用规则中还介绍了字符串中的大括号
- 令牌替换使用 replace 运算符
- 该方法
$ExecutionContext.InvokeCommand.ExpandString()具有 .NET API 参考文档