当前位置:网站首页 > 更多 > 玩电脑 > 正文

[玩转系统] 您想了解的有关字符串中变量替换的所有信息

作者:精品下载站 日期:2024-12-14 03:04:21 浏览:14 分类:玩电脑

您想了解的有关字符串中变量替换的所有信息


在字符串中使用变量的方法有很多种。我称之为变量替换,但我指的是任何时候您想要格式化字符串以包含变量中的值的时候。这是我经常向新脚本编写者解释的事情。

笔记

本文的原始版本出现在@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"

您可能希望从 $directory 中获取 CreationTime,但是您却得到了这个 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-PathTest-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"

感谢 Reddit 用户 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 加载这些令牌。

执行上下文展开字符串

有一种巧妙的方法可以用单引号定义替换字符串并稍后扩展变量。看这个例子:

$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 类,有自己的文档
  • 字符串中的大括号也包含在引用规则中
  • 令牌替换使用替换运算符
  • $ExecutionContext.InvokeCommand.ExpandString() 方法具有 .NET API 参考文档

您需要 登录账户 后才能发表评论

取消回复欢迎 发表评论:

关灯